單點登錄(一):思考


單點登錄(后文簡稱:sso)的定義是在多個應用系統中,用戶只需要登錄一次就可以訪問所有相互信任的應用系統(摘自百度百科)。整個流程中涉及到的角色有:
  • 用戶。
  • 應用服務器,即業務系統。
  • 單點登錄服務器,所有業務系統登錄的核心樞紐,后文簡稱用戶中心。
 
關於token同步的思考
從其定義中不難發現,核心功能點: 一處登錄處處登錄,注銷亦然。那么如何實現一處登錄處處登錄,先拋開網上各種解決思路回到問題本身。用戶中心登錄成功后產生的token(或者說“票據”,后文統一稱token)如果能夠同步到各個業務系統,而各個業務系統能成功解析token后即可認為達到了一處登錄處處登錄。所以關鍵問題在於:
  1. 如何在用戶中心登錄成功后將token同步到各個業務系統。
  2. 各業務系統如何能夠成功解析token。
 
其中各業務系統解析token很好解決,和用戶中心約定一套公用的加密/解密方式即可。那么問題一,由於token的存儲一般在於瀏覽器,而從用戶中心服務器發起請求到各個業務系統是在瀏覽器端寫不了token的。那么換種思路, 在登錄成功后從瀏覽器端向各個業務系統發起請求寫入token。
 
關於登錄功能使用的思考
而由於用戶中心被許多業務系統所使用,各系統所使用的開發語言未必能完全統一,於是有功能點二: 登錄服務的調用應該是易用且與平台語言無關的。這個問題可按兩種不同的思路來解決:
  1. 業務系統沒有登錄頁面,直接跳轉用戶中心登錄並將token同步至所有業務系統。
  2. 業務系統有登錄頁面,直接引用用戶中心sso.js調用登錄並將token同步至所有業務系統。
 
關於登錄用戶權限的思考
假定有業務系統A、B、C、D。用戶1可登錄系統A、B,用戶2可登錄系統B、C、D,於是有功能點三: 用戶中心應該可以控制用戶所能登錄的業務系統。在登錄生成token時,加入能夠登錄的業務系統信息,在登錄成功后,只向能夠登錄的業務系統發起同步token的請求,並且各業務系統在token解析后需要驗證token是否具有當前系統的登錄權限。
 
關於token刷新策略的思考
關於token的刷新策略,token應該什么時候刷新,在sso系統中,token刷新后又該如何通知到其他業務系統。第一個問題參考owin的cookie登錄,在請求中,判斷token是否超過有效期的一半,超過則刷新。第二個問題就麻煩了,因為token的刷新是跟隨正常請求的,我們就不能再使用像登錄那樣依靠瀏覽器去通知所有業務系統了,關於這個問題,有三種解決思路:
  1. 各系統定時刷新token並通知各個業務系統。
  2. token只存於用戶中心,向各個業務系統發放該token的key,各業務系統根據key向用戶中心獲取token並緩存,緩存的過期時間為是token下次應該刷新的時間。
  3. 共享一個分布式token存儲系統,可使用redis,向各個業務系統發放token的key,需要刷新時直接使用key刷新redis中的token。
 
巴拉巴拉講了一堆,也不知道大伙們能理解多少,權當記錄我在開發過程中的一些思考吧,當然少不了大家喜聞樂見的GitHub地址: https://github.com/liuxx001/sso.git,下篇講具體實現,最后先放個sso.js壓壓驚。
var sso = sso || {};
(function ($) {
    sso.host = "http://localhost:58806/";

    sso.utils = {
        isEmpty: function(str) {
            if (typeof (str) === "undefined") return true;
            if (str.replace(/(^s*)|(s*$)/g, "").length === 0) return true;
            return false;
        }
    };

    /**
    * 登錄
    * @param {signInfo}登錄信息
    *   {
            userName:"",
            password:"",
            rememberMe:false,
            returnUrl:""
        }
    */
    sso.login = function(signInfo) {
        if (sso.utils.isEmpty(signInfo.userName)) {
            alert("用戶名不能為空");
            return;
        }
        if (sso.utils.isEmpty(signInfo.password)) {
            alert("登錄密碼不能為空");
            return;
        }
        $.ajax({
            url: sso.host + "Account/SignIn",
            dataType: 'jsonp',
            type: 'GET',
            contentType: 'application/json',
            data: signInfo
        });
    };

    /**
    * 三方登錄
    * @param {signInfo}登錄信息
    *   {
            loginProvider:"",
            providerKey:"",
            rememberMe:false,
            returnUrl:""
        }
    */
    sso.externalLogin = function(signInfo) {
        if (sso.utils.isEmpty(signInfo.loginProvider)) {
            alert("三方登錄來源不能為空");
            return;
        }
        if (sso.utils.isEmpty(signInfo.providerKey)) {
            alert("三方登錄唯一Key不能為空");
            return;
        }
        $.ajax({
            url: sso.host + "Account/ExternalSignIn",
            dataType: 'jsonp',
            type: 'GET',
            contentType: 'application/json',
            data: signInfo
        });
    };

    /**
     * 注銷
     */
    sso.logOut = function() {
        $.ajax({
            url: sso.host + "Account/SignOut",
            dataType: 'jsonp',
            type: 'GET',
            contentType: 'application/json',
            data: {}
        });
    };

    /**
     * sso服務器登錄成功后jsonp回調
     * @param {string[]}需要通知的Url集合
     */
    sso.notify = function () {
        var createScript = function (src) {
            $("<script><//script>").attr("src", src).appendTo("body");
        };

        var urlList = arguments;
        for (var i = 1; i < urlList.length; i++) {
            createScript(urlList[i]);
        }

        //延時執行,避免跳轉時cookie還未寫入成功
        setTimeout(function () {
            if (urlList[0] === "refresh") {
                window.location.reload();
            } else {
                window.location.href = urlList[0];
            }
        }, 1000);
    };

    /**
     * sso服務器登錄失敗后jsonp回調
     * @param {code}錯誤碼
     * @param {msg}錯誤消息
     */
    sso.error= function(code, msg) {
        alert(msg);
    }
})(jQuery);

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM