SSO單點登錄詳解-------五、手寫單點登錄客戶端和服務端單點註銷

一、前言

單點登錄自然也要單點註銷,在一個子系統中註銷,所有子系統的會話都將被銷燬,用下面的圖來說明。單點註銷難點在於在其中一個系統註銷之後,需要把其他的子系統的會話銷燬.所以肯定需要子系統在令牌校驗通過之後,統一認證中心要把該子系統的地址和會話記錄起來.才能在註銷的時候找到這些子系統通,依次調用子系統通的註銷方法,銷燬局部會話.

二、單點註銷流程圖

在這裏插入圖片描述

單點註銷流程

在這裏插入圖片描述

cookie和session存儲結構

三、代碼實現

客戶端(注意:兩個客戶端項目都得改):

步驟:
1.在兩個客戶端項目中修改main.jsp,讓退出的地址映射到統一認證中心的登出方法.

<a href="http://www.sso.com:8443/logOut" >退出</a>

2.在SSOClientFilter.java校驗令牌信息token的時候,還需要把該系統的登出地址和該系統的會話id一併的發送到統一認證中心.修改SSOClientFilter.javadoFilter方法

    @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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章