作者:藍(lán)雄威,叩丁狼教育高級講師宛琅。原創(chuàng)文章联逻,轉(zhuǎn)載請注明出處。
一苹丸、前言
我們自己動手寫單點(diǎn)登錄的服務(wù)端目的是為了加深對單點(diǎn)登錄的理解.如果你們公司想實(shí)現(xiàn)單點(diǎn)登錄/單點(diǎn)注銷功能,推薦使用開源的單點(diǎn)登錄框架CAS.我們后面的章節(jié)也會帶同學(xué)們快速搭建CAS Server和CAS Client的環(huán)境.
二、條件
如果沒看前面章節(jié)的同學(xué),請返回去觀看這幾章內(nèi)容,不然這代碼是不太好理解的.
- SSO單點(diǎn)登錄教程(一)多系統(tǒng)的復(fù)雜性
- SSO單點(diǎn)登錄教程(二)單點(diǎn)登錄流程分析
- SSO單點(diǎn)登錄教程(三)單點(diǎn)注銷流程分析
三苇经、環(huán)境要求
- JDK1.7+
- Maven3.3
- Eclipse/IDEA
四赘理、準(zhǔn)備工作
因?yàn)槲覀冎饕v的是跨域的單點(diǎn)登錄,所以我們需要把不同項(xiàng)目部署到不同域名下.不可能為了完成這個代碼,讓同學(xué)們?nèi)グ⒗镌瀑I三臺主機(jī),映射三個IP.所以我們的實(shí)驗(yàn)就在本機(jī)來實(shí)現(xiàn).我們需要修改host文件,讓三個域名映射到本機(jī).
host文件存放的位置:C:\Windows\System32\drivers\etc
打開host文件之后,在最后追加如下配置:
127.0.0.1 www.sso.com
127.0.0.1 www.crm.com
127.0.0.1 www.wms.com
這段配置的意思是,我們在瀏覽器中輸入:
http://www.sso.com
http://www.crm.com
http://www.wms.com
其實(shí)訪問的都是本機(jī):127.0.0.1
PS:有些同學(xué)打開這個文件之后,保存的時候可能被拒絕.原因可能是權(quán)限不夠.解決方法:把host文件拷貝到桌面(有權(quán)限的地方即可),修改好之后再把:C:\Windows\System32\drivers\etc
的host文件覆蓋.
五滥朱、下載基礎(chǔ)項(xiàng)目
基礎(chǔ)項(xiàng)目代碼下載鏈接在頁面底部.
我在github上傳的是maven結(jié)構(gòu)的項(xiàng)目.如果需要導(dǎo)入到Eclipse/IDEA中需要生成對應(yīng)的Eclipse/IDEA的配置文件.
cmd
命令進(jìn)入到項(xiàng)目的根目錄 $項(xiàng)目存放位置/sso-server-base-project
- 如果是Eclipse,運(yùn)行
mvn eclipse:eclipse
- 如果是IDEA,運(yùn)行
mvn idea:idea
處理好之后,把項(xiàng)目導(dǎo)入到工具中,我們就可以開始開發(fā)了.
六鞠呈、項(xiàng)目結(jié)構(gòu)說明
服務(wù)端
sso-server-base-project目錄
src
main
java
resources
-applicationContext.xml
webapp
static
WEB-INF
views
-login.jsp
-logOut.jsp
-web.xml
-pom.xml
服務(wù)端項(xiàng)目就只配置了SpringMVC的環(huán)境.
pom.xml
:項(xiàng)目的pom文件,已經(jīng)配置的Tomcat插件端口為:8443
applicationContext.xml
:spring配置文件
static
:靜態(tài)資源目錄,存放css,js
login.jsp
:登陸頁面
logOut.jsp
:登出頁面
web.xml
:web的配置文件,配置前端請求DispatherServlet
客戶端
sso-client-base-project目錄
src
main
java
-cn.wolfcode.sso.controller.MainServlet.java
-cn.wolfcode.sso.controller.LogOutServlet.java
webapp
WEB-INF
views
-main.jsp
-web.xml
客戶端沒有使用Spring框架.使用Servlet3.0
@WebServlet(name = "mainServlet", urlPatterns = "/main")
在Servlet類上貼這個注解就可以進(jìn)行映射.
MainServlet.java
:處理主頁請求/main的servlet.
LogOutServlet.java
:處理登出的請求/logOut的servlet
main.jsp
:首頁
客戶端項(xiàng)目導(dǎo)入之后,運(yùn)行tomcat7:run
命令,在瀏覽器中輸入
http://www.crm.com:8088/main
會看到如下界面:
七、執(zhí)行流程圖
我們代碼的開發(fā)就參考著單點(diǎn)登錄流程圖來實(shí)現(xiàn),所以我在這也把這張圖放過來.
八厦幅、代碼實(shí)現(xiàn)
準(zhǔn)備階段:
一:在resources目錄創(chuàng)建sso.properties
,內(nèi)容如下:
#統(tǒng)一認(rèn)證中心的地址
server-url-prefix=http://www.sso.com:8443
#本項(xiàng)目的地址
client-host-url=http://www.crm.com:8088
二:添加工具類.
我們在后續(xù)的開發(fā)中需要使用這個工具類,寫得比較簡單,可以先看看,我們用到再給同學(xué)們解釋啥意思.
SSOClientUtil.java
package cn.wolfcode.sso.util;
import java.io.IOException;
import java.util.Properties;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class SSOClientUtil {
private static Properties ssoProperties = new Properties();
public static String SERVER_URL_PREFIX;//統(tǒng)一認(rèn)證中心地址:http://www.sso.com:8443,在sso.properties配置
public static String CLIENT_HOST_URL;//當(dāng)前客戶端地址:http://www.crm.com:8088,在sso.properties配置
static{
try {
ssoProperties.load(SSOClientUtil.class.getClassLoader().getResourceAsStream("sso.properties"));
} catch (IOException e) {
e.printStackTrace();
}
SERVER_URL_PREFIX = ssoProperties.getProperty("server-url-prefix");
CLIENT_HOST_URL = ssoProperties.getProperty("client-host-url");
}
/**
* 當(dāng)客戶端請求被攔截,跳往統(tǒng)一認(rèn)證中心,需要帶redirectUrl的參數(shù),統(tǒng)一認(rèn)證中心登錄后回調(diào)的地址
* 通過Request獲取這次請求的地址 http://www.crm.com:8088/main
*
* @param request
* @return
*/
public static String getRedirectUrl(HttpServletRequest request){
//獲取請求URL
return CLIENT_HOST_URL+request.getServletPath();
}
/**
* 根據(jù)request獲取跳轉(zhuǎn)到統(tǒng)一認(rèn)證中心的地址 http://www.sso.com:8443//checkLogin?redirectUrl=http://www.crm.com:8088/main
* 通過Response跳轉(zhuǎn)到指定的地址
* @param request
* @param response
* @throws IOException
*/
public static void redirectToSSOURL(HttpServletRequest request,HttpServletResponse response) throws IOException {
String redirectUrl = getRedirectUrl(request);
StringBuilder url = new StringBuilder(50)
.append(SERVER_URL_PREFIX)
.append("/checkLogin?redirectUrl=")
.append(redirectUrl);
response.sendRedirect(url.toString());
}
/**
* 獲取客戶端的完整登出地址 http://www.crm.com:8088/logOut
* @return
*/
public static String getClientLogOutUrl(){
return CLIENT_HOST_URL+"/logOut";
}
/**
* 獲取認(rèn)證中心的登出地址 http://www.sso.com:8443/logOut
* @return
*/
public static String getServerLogOutUrl(){
return SERVER_URL_PREFIX+"/logOut";
}
}
HttpUtil.java
package cn.wolfcode.sso.util;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.charset.Charset;
import java.util.Map;
import java.util.Map.Entry;
import org.springframework.util.StreamUtils;
public class HttpUtil {
/**
* 模擬瀏覽器的請求
* @param httpURL 發(fā)送請求的地址
* @param params 請求參數(shù)
* @return
* @throws Exception
*/
public static String sendHttpRequest(String httpURL,Map<String,String> params) throws Exception{
//建立URL連接對象
URL url = new URL(httpURL);
//創(chuàng)建連接
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
//設(shè)置請求的方式(需要是大寫的)
conn.setRequestMethod("POST");
//設(shè)置需要輸出
conn.setDoOutput(true);
//判斷是否有參數(shù).
if(params!=null&¶ms.size()>0){
StringBuilder sb = new StringBuilder();
for(Entry<String,String> entry:params.entrySet()){
sb.append("&").append(entry.getKey()).append("=").append(entry.getValue());
}
//sb.substring(1)去除最前面的&
conn.getOutputStream().write(sb.substring(1).toString().getBytes("utf-8"));
}
//發(fā)送請求到服務(wù)器
conn.connect();
//獲取遠(yuǎn)程響應(yīng)的內(nèi)容.
String responseContent = StreamUtils.copyToString(conn.getInputStream(),Charset.forName("utf-8"));
conn.disconnect();
return responseContent;
}
/**
* 模擬瀏覽器的請求
* @param httpURL 發(fā)送請求的地址
* @param jesssionId 會話Id
* @return
* @throws Exception
*/
public static void sendHttpRequest(String httpURL,String jesssionId) throws Exception{
//建立URL連接對象
URL url = new URL(httpURL);
//創(chuàng)建連接
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
//設(shè)置請求的方式(需要是大寫的)
conn.setRequestMethod("POST");
//設(shè)置需要輸出
conn.setDoOutput(true);
conn.addRequestProperty("Cookie","JSESSIONID="+jesssionId);
//發(fā)送請求到服務(wù)器
conn.connect();
conn.getInputStream();
conn.disconnect();
}
}
階段一:
階段一代碼下載鏈接在頁面底部.
第一階段我們先完成,攔截客戶端的請求,判斷是否有局部會話,沒有局部會話就重定向到統(tǒng)一認(rèn)證中心的登陸界面.
需求分析:
我們要在客戶端攔截請求,應(yīng)該使用啥技術(shù)呢?如果使用的是Spring框架,我們可以使用攔截器.但我們的客戶端啥框架都沒用.要攔截請求,可以使用過濾器Filter
.
客戶端
創(chuàng)建:SSOClientFilter.java
,實(shí)現(xiàn)javax.servlet.Filter
接口,并貼上Servlet3.0的注解
@WebFilter(filterName="SSOClientFilter",urlPatterns="/*")
public class SSOClientFilter implements Filter {
....
}
步驟:
1.判斷是否有局部會話
2.如果有局部會話,直接放行
3.如果沒有,重定向到統(tǒng)一認(rèn)證中心的checkLogin方法,檢查是否有全局會話.
package cn.wolfcode.sso.filter;
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import cn.wolfcode.sso.util.SSOClientUtil;
@WebFilter(filterName="SSOClientFilter",urlPatterns="/*")
public class SSOClientFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {}
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse resp = (HttpServletResponse) response;
HttpSession session = req.getSession();
//1.判斷是否有局部的會話
Boolean isLogin = (Boolean) session.getAttribute("isLogin");
if(isLogin!=null && isLogin){
//有局部會話,直接放行.
chain.doFilter(request, response);
return;
}
//沒有局部會話,重定向到統(tǒng)一認(rèn)證中心,檢查是否有其他的系統(tǒng)已經(jīng)登錄過.
// http://www.sso.com:8443/checkLogin?redirectUrl=http://www.crm.com:8088
//這是我們自己寫工具類的方法,同學(xué)們可以自己看一下,很簡單能看懂的.
SSOClientUtil.redirectToSSOURL(req, resp);
}
@Override
public void destroy() {}
}
服務(wù)端
步驟:
1.接受重定向過來的checkLogin請求.判斷是否有全局的會話
2.如果沒有全局會話,獲取地址欄的redirectUrl參數(shù),放入到request域中.并轉(zhuǎn)發(fā)到登陸頁面.
3.如果有全局會話,目前還沒到這個階段,這個邏輯我們先不寫.我們先按執(zhí)行流程來寫代碼.
在java目錄創(chuàng)建SSOServerController.java
,并貼上@Controller
注解
@Controller
public class SSOServerController {
}
編寫checkLogin方法.
package cn.wolfcode.sso.controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import javax.servlet.http.HttpSession;
/**
* Created by wolfcode-lanxw
*/
@Controller
public class SSOServerController {
/**
* 檢查是否有全局會話.
* @param redirectUrl 客戶端被攔截的請求地址
* @param session 統(tǒng)一認(rèn)證中心的會話對象
* @param model 數(shù)據(jù)模型
* @return 視圖地址
*/
@RequestMapping("/checkLogin")
public String checkLogin(String redirectUrl, HttpSession session, Model model){
//1.判斷是否有全局的會話
//從會話中獲取令牌信息,如果取不到說明沒有全局會話,如果能取到說明有全局會話
String token = (String) session.getAttribute("token");
if(StringUtils.isEmpty(token)){
//表示沒有全局會話
model.addAttribute("redirectUrl",redirectUrl);
//跳轉(zhuǎn)到統(tǒng)一認(rèn)證中心的登陸頁面.已經(jīng)配置視圖解析器,
// 會找/WEB-INF/views/login.jsp視圖
return "login";
}else{
//有全局會話
//目前這段邏輯我們先不寫,按著圖解流程編寫代碼
return "";
}
}
}
測試:
服務(wù)端和客戶端代碼寫好之后,兩個項(xiàng)目都運(yùn)行tomcat7:run
的命令.
在瀏覽器地址欄輸入:
www.crm.com:8088/main
發(fā)現(xiàn)我們的這個請求被攔截了,跳轉(zhuǎn)到了統(tǒng)一認(rèn)證中心的登陸界面.如下圖所示:
階段二:
基礎(chǔ)項(xiàng)目代碼下載鏈接在頁面底部.
服務(wù)端:
步驟:
1.編寫登陸方法,實(shí)現(xiàn)認(rèn)證功能.
2.認(rèn)證通過,創(chuàng)建令牌.
3.創(chuàng)建全局會話存儲令牌信息
4.把令牌存入到數(shù)據(jù)庫t_token表中.
為了減低學(xué)習(xí)的難度,我們這個案例里面就不去連接數(shù)據(jù)庫(當(dāng)然要連接數(shù)據(jù)庫也不難),我們的認(rèn)證就使用靜態(tài)的認(rèn)證(賬戶名:zhangsan,密碼:666).
我們使用java中的Set
集合來模擬t_token表.
創(chuàng)建MockDatabaseUtil.java
來模擬數(shù)據(jù)庫
package cn.wolfcode.sso.util;
import java.util.*;
/**
* Created by wolfcode-lanxw
*/
public class MockDatabaseUtil {
//模擬數(shù)據(jù)庫中的t_token表
public static Set<String> T_TOKEN = new HashSet<String>();
}
編寫統(tǒng)一認(rèn)證中心的登陸方法,在SSOServerController.java
中添加login
方法.
/**
* 登陸方法
* @param username 前臺登陸的用戶名
* @param password 前臺登陸的密碼
* @param redirectUrl 客戶端被攔截的地址
* @param session 服務(wù)端會話對象
* @param model 模型數(shù)據(jù)
* @return 響應(yīng)的視圖地址
*/
@RequestMapping("/login")
public String login(String username,String password,String redirectUrl,HttpSession session,Model model){
if("zhangsan".equals(username)&&"666".equals(password)){
//賬號密碼匹配
//1.創(chuàng)建令牌信息,只要保證唯一即可,我們就使用UUID.
String token = UUID.randomUUID().toString();
//2.創(chuàng)建全局的會話,把令牌信息放入會話中.
session.setAttribute("token",token);
//3.需要把令牌信息放到數(shù)據(jù)庫中.
MockDatabaseUtil.T_TOKEN.add(token);
//4.重定向到redirectUrl,把令牌信息帶上. http://www.crm.com:8088/main?token=
model.addAttribute("token",token);
return "redirect:"+redirectUrl;
}
//如果賬號密碼有誤,重新回到登錄頁面,還需要把redirectUrl放入request域中.
model.addAttribute("redirectUrl",redirectUrl);
return "login";
}
客戶端:
1.統(tǒng)一認(rèn)證中心登錄成功之后,會重定向到之前客戶端被攔截的地址,并會把令牌信息在地址欄中作為參數(shù)http://www.crm.com:8088/main?token=VcnVMguCDWJX5zHa
此時訪問的是客戶端的地址,這個地址會被SSOClientFilter
攔截到.
我們在Filter里面需要判斷用戶地址欄中是否有攜帶token信息,如果有,說明擁有令牌信息.但是我們得校驗(yàn)令牌token的有效性,使用HttpUrlConnection發(fā)送請求到統(tǒng)一認(rèn)證中心進(jìn)行校驗(yàn).
2.如果統(tǒng)一認(rèn)證中心給我們返回true,表示令牌有效.
3.我們創(chuàng)建局部會話,并放行請求.
在SSOClientFilter.java
中添加如下代碼
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse resp = (HttpServletResponse) response;
HttpSession session = req.getSession();
//1.判斷是否有局部的會話
Boolean isLogin = (Boolean) session.getAttribute("isLogin");
if(isLogin!=null && isLogin){
//有局部會話,直接放行.
chain.doFilter(request, response);
return;
}
/**-------------------------階段二添加的代碼start---------------------------------**/
//判斷地址欄中是否有攜帶token參數(shù).
String token = req.getParameter("token");
if(StringUtils.isNoneBlank(token)){
//token信息不為null,說明地址中包含了token,擁有令牌.
//判斷token信息是否由認(rèn)證中心產(chǎn)生的.
//驗(yàn)證地址為:http://www.sso.com:8443/verify
String httpURL = SSOClientUtil.SERVER_URL_PREFIX+"/verify";
Map<String,String> params = new HashMap<String,String>();
//把客戶端地址欄添加到的token信息傳遞給統(tǒng)一認(rèn)證中心進(jìn)行校驗(yàn)
params.put("token", token);
try {
String isVerify = HttpUtil.sendHttpRequest(httpURL, params);
if("true".equals(isVerify)){
//如果返回的字符串是true,說明這個token是由統(tǒng)一認(rèn)證中心產(chǎn)生的.
//創(chuàng)建局部的會話.
session.setAttribute("isLogin", true);
//放行該次的請求
chain.doFilter(request, response);
return;
}
} catch (Exception e) {
//這里可以完善,比如出現(xiàn)異常在前臺顯示具體頁面
//我們這個案例就不做這個哈.
e.printStackTrace();
}
}
/**-------------------------階段二添加的代碼end---------------------------------**/
//沒有局部會話,重定向到統(tǒng)一認(rèn)證中心,檢查是否有其他的系統(tǒng)已經(jīng)登錄過.
// http://www.sso.com:8443/checkLogin?redirectUrl=http://www.crm.com:8088
SSOClientUtil.redirectToSSOURL(req, resp);
}
服務(wù)端:
1.需要在統(tǒng)一認(rèn)證中心添加一個認(rèn)證令牌信息的方法.
在SSOServerController.java
中添加verifyToken
方法,具體代碼如下:
/**
* 校驗(yàn)客戶端傳過來的令牌信息是否有效
* @param token 客戶端傳過來的令牌信息
* @return
*/
@RequestMapping("/verify")
@ResponseBody
public String verifyToken(String token){
//在模擬的數(shù)據(jù)庫表t_token中查找是否有這條記錄
if(MockDatabaseUtil.T_TOKEN.contains(token)){
//說明令牌有效,返回true
return "true";
}
return "false";
}
測試:
到這里為止,階段二代碼就搞定了.單點(diǎn)登錄功能的95%代碼完成.
客戶端和服務(wù)端都運(yùn)行tomcat7:run
命令
在瀏覽器中按下Ctrl+Shift+Delete
按鍵清楚cookie和緩存,避免干擾.
在瀏覽器中輸入:http://www.crm.com:8088/main
,瀏覽器跳轉(zhuǎn)到統(tǒng)一認(rèn)證中心的登陸頁面.輸入zhangsan:666
,點(diǎn)擊登陸.此時就訪問到了CRM系統(tǒng)的首頁.界面如下.
階段三:
階段三代碼下載鏈接在頁面底部.
在前面的代碼我們完成了單系統(tǒng)的登陸,現(xiàn)在我們先看看如果在多系統(tǒng)的環(huán)境下,我們是否能實(shí)現(xiàn)多系統(tǒng)的下一次登陸,處處運(yùn)行的功能.
客戶端:
1.拷貝sso-client-base-project
項(xiàng)目,命名為sso-client-base-project2
2.修改新項(xiàng)目的pom.xml
文件第41行,Tomcat插件的啟動端口,修改為:8089
3.修改sso.properties
文件,修改如下:
server-url-prefix=http://www.sso.com:8443
client-host-url=http://www.wms.com:8089
4.修改/WEB-INF/views/main.jsp
的標(biāo)題,和內(nèi)容,主要方便測試的時候看到不同的效果.(可改可不改)
服務(wù)端:
需要完善checkLogin
方法,添加如果有全局會話的邏輯.
@RequestMapping("/checkLogin")
public String checkLogin(String redirectUrl, HttpSession session, Model model){
//1.判斷是否有全局的會話
//從會話中獲取令牌信息,如果取不到說明沒有全局會話,如果能取到說明有全局會話
String token = (String) session.getAttribute("token");
if(StringUtils.isEmpty(token)){
//表示沒有全局會話
model.addAttribute("redirectUrl",redirectUrl);
//跳轉(zhuǎn)到統(tǒng)一認(rèn)證中心的登陸頁面.已經(jīng)配置視圖解析器,
// 會找/WEB-INF/views/login.jsp視圖
return "login";
}else{
/**---------------------------階段三添加的代碼start--------------------**/
//有全局會話
//取出令牌信息,重定向到redirectUrl,把令牌帶上
// http://www.wms.com:8089/main?token=
model.addAttribute("token",token);
/**---------------------------階段三添加的代碼end-----------------------**/
return "redirect:"+redirectUrl;
}
}
測試:
在服務(wù)端和兩個客戶端運(yùn)行tomcat7:run
命令.
在瀏覽器中按下Ctrl+Shift+Delete
按鍵清楚cookie和緩存,避免干擾.
在瀏覽器中輸入:http://www.crm.com:8088/main
,瀏覽器跳轉(zhuǎn)到統(tǒng)一認(rèn)證中心的登陸頁面.輸入zhangsan:666
,點(diǎn)擊登陸.此時就訪問到了CRM系統(tǒng)的首頁.說明已經(jīng)登錄成功.
接著瀏覽器中輸入:http://www.wms.com:8089/main
,發(fā)現(xiàn)這次請求就不需要登陸,可以直接訪問了.到此為止,我們就完成單點(diǎn)登錄所有的代碼.可以實(shí)現(xiàn)一次登陸,處處穿梭.
九蜘澜、單點(diǎn)登錄步驟梳理:
客戶端
1.攔截客戶端的請求判斷是否有局部的session
2.1如果有局部的session,放行請求.
2.2如果沒有局部session
2.2.1請求中有攜帶token參數(shù)
2.2.1.1如果有,使用HttpURLConnection發(fā)送請求校驗(yàn)token是否有效.
2.2.1.1.1如果token有效,建立局部的session.
2.2.1.1.2如果token無效,重定向到統(tǒng)一認(rèn)證中心頁面進(jìn)行登陸.
2.2.1.2如果沒有,重定向到統(tǒng)一認(rèn)證中心頁面進(jìn)行登陸.
2.2.2請求中沒有攜帶token參數(shù),重定向到統(tǒng)一認(rèn)證中心頁面進(jìn)行登陸.
服務(wù)端
1.檢測客戶端在服務(wù)端是否已經(jīng)登錄了.(checkLogin方法)
1.1獲取session中的token.
1.2如果token不為空,說明服務(wù)端已經(jīng)登錄過了,此時重定向到客戶端的地址,并把token帶上
1.3如果token為空,跳轉(zhuǎn)到統(tǒng)一認(rèn)證中心的的登錄頁面,并把redirectUrl放入到request域中.
2.統(tǒng)一認(rèn)證中心的登錄方法(login方法)
2.1判斷用戶提交的賬號密碼是否正確.
2.2如果正確
2.2.1創(chuàng)建token(可以使用UUID,保證唯一就可以)
2.2.2把token放入到session中,還需要把token放入到數(shù)據(jù)庫表t_token中
2.2.3這個token要知道有哪些客戶端登陸了,存入數(shù)據(jù)庫t_client_info表中.);
2.2.4轉(zhuǎn)發(fā)到redirectUrl地址,把token帶上.
2.3如果錯誤
轉(zhuǎn)發(fā)到login.jsp,還需要把redirectUrl參數(shù)放入到request域中.
3.統(tǒng)一認(rèn)證中心認(rèn)證token方法(verifyToken方法),返回值為String,貼@ResponseBody
3.1如果MockDatabaseUtil.T_TOKEN.contains(token)結(jié)果為true,說明token是有效的.
3.1.1返回true字符串.
3.1如果MockDatabaseUtil.T_TOKEN.contains(token)結(jié)果為false,說明token是無效的,返回false字符串.
十施流、代碼下載
0.初始項(xiàng)目Demo
熟悉git命令的同學(xué):
客戶端的基礎(chǔ)項(xiàng)目:
git clone git@github.com:javalanxiongwei/sso-client-base-project.git
cd sso-client-base-project/
git reset --hard 8401333ea845eb32e5f6091e7326ada1983d1ea3
服務(wù)頓的基礎(chǔ)項(xiàng)目:
git clone git@github.com:javalanxiongwei/sso-server-base-project.git
cd sso-server-base-project/
git reset --hard 6334d9afa08b3d5fc886ad212b3ec62376f5ff32
不熟悉git命令的同學(xué)
客戶端的基礎(chǔ)項(xiàng)目
服務(wù)端的基礎(chǔ)項(xiàng)目
1.階段一Demo
熟悉git命令的同學(xué):
客戶端階段一:
git reset --hard b53e0234895b2044ed3042f8f856676c69160281
服務(wù)頓階段一:
git reset --hard 0ee718f408ef82d230fbc61c63b07b29b1277e45
不熟悉git命令的同學(xué)
2.階段二Demo
熟悉git命令的同學(xué):
客戶端階段二:
git reset --hard b53e0234895b2044ed3042f8f856676c69160281
服務(wù)頓階段二:
git reset --hard 0ee718f408ef82d230fbc61c63b07b29b1277e45
不熟悉git命令的同學(xué)
3.階段三Demo
熟悉git命令的同學(xué):
客戶端2階段三下載:
git clone git@github.com:javalanxiongwei/sso-client-base-project2.git
cd sso-client-base-project2/
git reset --hard 01db6af390ff9f765121d3f9e9b1895b0e671bd5
服務(wù)頓階段三:
git reset --hard 80e7ad5a1d67b5d63d00e3532fed9ef58fe74fd9