一、前言
單點登錄自然也要單點註銷,在一個子系統中註銷,所有子系統的會話都將被銷燬,用下面的圖來說明。單點註銷難點在於在其中一個系統註銷之後,需要把其他的子系統的會話銷燬.所以肯定需要子系統在令牌校驗通過之後,統一認證中心要把該子系統的地址和會話記錄起來.才能在註銷的時候找到這些子系統通,依次調用子系統通的註銷方法,銷燬局部會話.
二、單點註銷流程圖
三、代碼實現
客戶端(注意:兩個客戶端項目都得改):
步驟:
1.在兩個客戶端項目中修改main.jsp
,讓退出的地址映射到統一認證中心的登出方法.
<a href="http://www.sso.com:8443/logOut" >退出</a>
2.在SSOClientFilter.java
校驗令牌信息token的時候,還需要把該系統的登出地址和該系統的會話id一併的發送到統一認證中心.修改SSOClientFilter.java
的doFilter
方法
@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;
}
//判斷地址欄中是否有攜帶token參數.
String token = req.getParameter("token");
if(StringUtils.isNoneBlank(token)){
//token信息不爲null,說明地址中包含了token,擁有令牌.
//判斷token信息是否由認證中心產生的.
//驗證地址爲:http://www.sso.com:8443/verify
String httpURL = SSOClientUtil.SERVER_URL_PREFIX+"/verify";
Map<String,String> params = new HashMap<String,String>();
//把客戶端地址欄添加到的token信息傳遞給統一認證中心進行校驗
params.put("token", token);
/**-------------------------階段四添加的代碼start---------------------------------**/
//獲取客戶端的完整登出地址 http://www.crm.com:8088/logOut
params.put("clientUrl", SSOClientUtil.getClientLogOutUrl());
params.put("jsessionid", session.getId());
/**-------------------------階段四添加的代碼end---------------------------------**/
try {
String isVerify = HttpUtil.sendHttpRequest(httpURL, params);
if("true".equals(isVerify)){
//如果返回的字符串是true,說明這個token是由統一認證中心產生的.
//創建局部的會話.
session.setAttribute("isLogin", true);
//放行該次的請求
chain.doFilter(request, response);
return;
}
} catch (Exception e) {
//這裏可以完善,比如出現異常在前臺顯示具體頁面
//我們這個案例就不做這個哈.
e.printStackTrace();
}
}
//沒有局部會話,重定向到統一認證中心,檢查是否有其他的系統已經登錄過.
// http://www.sso.com:8443/checkLogin?redirectUrl=http://www.crm.com:8088
SSOClientUtil.redirectToSSOURL(req, resp);
}
服務端:
步驟:
1.在MockDatabaseUtil.java
中模擬表t_client_info存儲已註冊的子系統信息,創建ClientInfoVo.java
封裝客戶端信息
package cn.wolfcode.sso.vo;
import lombok.Getter;
import lombok.Setter;
/**
* Created by wolfcode-lanxw
*/
@Setter@Getter
public class ClientInfoVo {
private String clientUrl;//客戶端登出地址
private String jsessionid;//客戶端會話id
}
在MockDatabaseUtil.java
中模擬表t_client_info的信息.
public class MockDatabaseUtil {
//模擬數據庫中的t_token表
public static Set<String> T_TOKEN = new HashSet<String>();
//模擬數據庫中的t_client_info表
public static Map<String,List<ClientInfoVo>> T_CLIENT_INFO =new HashMap<String,List<ClientInfoVo>>();
}
2.需要在SSOServerController.java
修改verifyToken
方法的邏輯,獲取客戶端傳過來的令牌信息(token),客戶端登出地址(clientUrl),客戶端的會話id(jsessionid),並且需要把客戶端地址存儲起來.
@RequestMapping("/verify")
@ResponseBody
public String verifyToken(String token,String clientUrl,String jsessionid){
//在模擬的數據庫表t_token中查找是否有這條記錄
if(MockDatabaseUtil.T_TOKEN.contains(token)){
//根據token獲取客戶端的登出地址記錄集合
List<ClientInfoVo> clientInfoList = MockDatabaseUtil.T_CLIENT_INFO.get(token);
if(clientInfoList==null){
//第一個系統註冊的時候獲取出來的集合空的,所以需要創建一個
clientInfoList = new ArrayList<ClientInfoVo>();
//創建好之後需要放入到map中,方便後續的獲取
MockDatabaseUtil.T_CLIENT_INFO.put(token,clientInfoList);
}
//封裝客戶端的信息
ClientInfoVo vo = new ClientInfoVo();
vo.setClientUrl(clientUrl);
vo.setJsessionid(jsessionid);
//把當前的客戶端地址註冊到集合中.
clientInfoList.add(vo);
//說明令牌有效,返回true
return "true";
}
return "false";
}
3.在SSOServerController.java
添加一個統一認證中心登錄的方法logOut
@RequestMapping("/logOut")
public String logOut(HttpSession session){
//銷燬全局會話
session.invalidate();
return "logOut";
}
4.拷貝之前客戶端工具類HttpUtil.java
到項目中
5.創建MySessionListener.java
,監聽session的銷燬事件,當全局會話銷燬的時候,獲取全局會話中的令牌信息token,通過token信息查詢t_client_info表,獲取已註冊的子系統集合,遍歷子系統集合,依次調用子系統的登出方法.
6.清空t_token表數據,清空t_client_info表數據
package cn.wolfcode.sso.listener;
import cn.wolfcode.sso.util.HttpUtil;
import cn.wolfcode.sso.util.MockDatabaseUtil;
import cn.wolfcode.sso.vo.ClientInfoVo;
import javax.servlet.annotation.WebListener;
import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;
import java.util.List;
/**
* Created by wolfcode-lanxw
*/
@WebListener
public class MySessionListener implements HttpSessionListener {
@Override
public void sessionCreated(HttpSessionEvent se) {
}
//監聽session的銷燬事件
@Override
public void sessionDestroyed(HttpSessionEvent se) {
HttpSession session = se.getSession();
//獲取會話中的令牌信息
String token = (String) session.getAttribute("token");
//刪除t_token表中的數據
MockDatabaseUtil.T_TOKEN.remove(token);
//刪除t_client_info表中的數據
List<ClientInfoVo> clientInfoVoList = MockDatabaseUtil.T_CLIENT_INFO.remove(token);
try{
for(ClientInfoVo vo:clientInfoVoList){
//獲取出注冊的子系統,依次調用子系統的登出的方法
HttpUtil.sendHttpRequest(vo.getClientUrl(),vo.getJsessionid());
}
}catch(Exception e){
e.printStackTrace();;
}
}
}
四、單點註銷流程梳理
客戶端:
1.在登陸的按鈕鏈接寫上統一認證中心的登出方法即可.
<a href=”http://www.sso.com/logOut”>退出</a>
2.校驗令牌信息的同時把客戶端登出地址(clientUrl)和客戶端會話id(jsession)一併的傳到統一認證中心
服務端:
1.編寫logOut方法,調用session.invalidate()
2.創建session的監聽器,在session的監聽器的銷燬方法寫如下邏輯
2.1獲取session中的token.
2.2根據token獲取所有客戶端的登出地址和會話id
2.3通過HttpUtil選項調用客戶端的登出方法.
五、測試
在服務端和兩個客戶端運行tomcat7:run
命令.
在瀏覽器中按下Ctrl+Shift+Delete
按鍵清楚cookie和緩存,避免干擾.
1.訪問http://www.crm.com:8088/main
,跳轉到統一認證中心登錄界面.
2.輸入賬號密碼,放行請求,訪問到CRM系統的main.jsp頁面.
3.在同一瀏覽器中打開多個窗口輸入http://www.wms.com:8089/main
,都是放行請求.
4.點擊CRM系統中的退出按鈕,顯示系統已註銷
5.在同一瀏覽器中打開http://www.wms.com:8089/main
,此時就跳轉到統一認證中心的登陸頁面.
如果測試結果和上述一致,說明單點註銷功能也完成了.
六、代碼下載
熟悉git命令的同學:
客戶端單點註銷Demo:
git reset --hard 5631a13ecfd0b73bfc27adaa34ba0fd6fe01b2fb
客戶端2單點註銷Demo:
git reset --hard 01db6af390ff9f765121d3f9e9b1895b0e671bd5
服務端單點註銷Demo:
git reset --hard 5d619c5065cabd83a16d5b4d34e3c89a5950fb5b
不熟悉git命令的同學
客戶端單點註銷Demo
客戶端2單點註銷Demo
服務端單點註銷Demo
原文鏈接:https://www.jianshu.com/p/667c8f0b514f