zoukankan      html  css  js  c++  java
  • 单点登录(一):思考

    单点登录(后文简称: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);
  • 相关阅读:
    this和$(this)的关系
    单色边表格
    php概率算法
    jQuery Ajax 参数解析
    拍拍CPS入门使用
    飞鸽端口被占
    浏览器调试工具技巧分享
    事件click,bind,click
    jQuery旋转插件—rotate
    利用谷歌API生成二维码
  • 原文地址:https://www.cnblogs.com/liuyh/p/6775089.html
Copyright © 2011-2022 走看看