zoukankan      html  css  js  c++  java
  • vue实现单点登录的N种方式

    最近项目停工了,RageFrame的学习暂时告一段落,这一篇给大家分享下有关单点登录的相关知识,并提供一些demo给大家参考,希望对想了解的朋友有一些帮助。

    话不多说,先上原理(借鉴地址:https://www.jianshu.com/p/613e44d4a464)

    点登录SSO(Single Sign On)说得简单点就是在一个多系统共存的环境下,用户在一处登录后,就不用在其他系统中登录,也就是用户的一次登录能得到其他所有系统的信任。单点登录在大型网站里使用得非常频繁,例如像阿里巴巴这样的网站,在网站的背后是成百上千的子系统,用户一次操作或交易可能涉及到几十个子系统的协作,如果每个子系统都需要用户认证,不仅用户会疯掉,各子系统也会为这种重复认证授权的逻辑搞疯掉。实现单点登录说到底就是要解决如何产生和存储那个信任,再就是其他系统如何验证这个信任的有效性,因此要点也就以下两个:

    • 存储信任

    • 验证信任

    如果一个系统做到了开头所讲的效果,也就算单点登录,单点登录有不同的实现方式,本文就列出我开发中所遇见过的实现方式。

    方法一:以Cookie作为凭证媒介

    最简单的单点登录实现方式,是使用cookie作为媒介,存放用户凭证。
    用户登录父应用之后,应用返回一个加密的cookie,当用户访问子应用的时候,携带上这个cookie,授权应用解密cookie并进行校验,校验通过则登录当前用户。

    Auth via cookie

    不难发现以上方式把信任存储在客户端的Cookie中,这种方式很容易令人质疑:

    • Cookie不安全

    • 不能跨域实现免登

    对于第一个问题,通过加密Cookie可以保证安全性,当然这是在源代码不泄露的前提下。如果Cookie的加密算法泄露,攻击者通过伪造Cookie则可以伪造特定用户身份,这是很危险的。
    对于第二个问题,更是硬伤。

    方法二:通过JSONP实现

    对于跨域问题,可以使用JSONP实现。
    用户在父应用中登录后,跟Session匹配的Cookie会存到客户端中,当用户需要登录子应用的时候,授权应用访问父应用提供的JSONP接口,并在请求中带上父应用域名下的Cookie,父应用接收到请求,验证用户的登录状态,返回加密的信息,子应用通过解析返回来的加密信息来验证用户,如果通过验证则登录用户。

    Auth via jsonp

    这种方式虽然能解决跨域问题,但是安全性其实跟把信任存储到Cookie是差不多的。如果一旦加密算法泄露了,攻击者可以在本地建立一个实现了登录接口的假冒父应用,通过绑定Host来把子应用发起的请求指向本地的假冒父应用,并作出回应。
    因为攻击者完全可以按照加密算法来伪造响应请求,子应用接收到这个响应之后一样可以通过验证,并且登录特定用户。

    方法三:通过页面重定向的方式

    最后一种介绍的方式,是通过父应用和子应用来回重定向中进行通信,实现信息的安全传递。
    父应用提供一个GET方式的登录接口,用户通过子应用重定向连接的方式访问这个接口,如果用户还没有登录,则返回一个的登录页面,用户输入账号密码进行登录。如果用户已经登录了,则生成加密的Token,并且重定向到子应用提供的验证Token的接口,通过解密和校验之后,子应用登录当前用户。

    Auth via redirect

    这种方式较前面两种方式,接解决了上面两种方法暴露出来的安全性问题和跨域的问题,但是并没有前面两种方式方便。
    安全与方便,本来就是一对矛盾。

    方法四:使用独立登录系统

    一般说来,大型应用会把授权的逻辑与用户信息的相关逻辑独立成一个应用,称为用户中心。
    用户中心不处理业务逻辑,只是处理用户信息的管理以及授权给第三方应用。第三方应用需要登录的时候,则把用户的登录请求转发给用户中心进行处理,用户处理完毕返回凭证,第三方应用验证凭证,通过后就登录用户。

     

    以上,就是我了解的单点登录的给个模式及原理,下面给大家上实战代码,这里我列举两种情况,分类给大家讲解和提供我对应的demo(以下理论参考https://www.cnblogs.com/tibos/p/5354000.html)

    环境1:a.xxx.com需要跟b.xxx.com实现跨域,这种比较简单,只需要设置cookie的域名关联域就可以了 cookie.Domain = "xxx.com",这样两个域名间的cookie就可以互相访问,实现跨域.

    demo地址展示:

    系统一:sso1.linheng.xyz

    系统二:sso2.linheng.xyz

    vue具体代码:

    先打入指令安装js-cookie

    npm i js-cookie -S

    然后写入登录页面

    <template>
      <div class="hello">
        <h1>{{ msg }}</h1>
        <button @click="handleLogin">点击登录</button>
        <button @click="rethome">返回首页</button>
      </div>
    </template>
    
    <script>
    import Cookies from 'js-cookie'
    export default {
      name: 'home',
      data () {
        return {
          msg: '系统一登录页面'
        }
      },
      methods: {
    		handleLogin() {
            var token = this.randomString();
            Cookies.set('app.token',token, { expires: 1000, path: '/', domain: '.**.com' })//这里换你的网站根目录
            Cookies.set('app.loginname','系统一', { expires: 1000, path: '/', domain: '.**.com' })//这里换你的网站根目录
            this.$router.push("/"); 
        },
        rethome(){
          this.$router.push("/"); 
        },
        randomString(e) {
            e = e || 32;
            var t = "ABCDEFGHJKMNPQRSTWXYZabcdefhijkmnprstwxyz2345678",
            a = t.length,
            n = "";
            for (var i = 0; i < e; i++) n += t.charAt(Math.floor(Math.random() * a));
            return n
        }
    	}
    }
    </script>
    
    <!-- Add "scoped" attribute to limit CSS to this component only -->
    <style scoped>
    h1, h2 {
      font-weight: normal;
    }
    ul {
      list-style-type: none;
      padding: 0;
    }
    li {
      display: inline-block;
      margin: 0 10px;
    }
    a {
      color: #42b983;
    }
    </style>

    接下来是首页:

    <template>
      <div class="hello">
        <h1>{{ msg }}</h1>
        <h3>用户信息为:{{token}}</h3>
        <h3>登录地点:{{loginname}}</h3>
        <button @click="logout">登出</button>
      </div>
    </template>
    
    <script>
    import Cookies from 'js-cookie'
    
    export default {
      name: 'home',
      data () {
        return {
          msg: '系统一主页面',
          token:'',
          loginname:''
        }
      },
      mounted(){
        const token = Cookies.get('app.token');
        this.token = token;
        const loginname = Cookies.get('app.loginname');
        this.loginname = loginname;
        console.log(token);
        if(!token){
          this.$router.push("/login");
        }
      },
      methods: {
        logout(){
             Cookies.set('app.token','', { expires: 1000, path: '/', domain: '.**.com' })//这里换你的网站根目录
            Cookies.set('app.loginname','', { expires: 1000, path: '/', domain: '.**.com' })//这里换你的网站根目录
            this.$router.go(0)
        }
    	}
    }
    </script>
    
    <!-- Add "scoped" attribute to limit CSS to this component only -->
    <style scoped>
    h1, h2 {
      font-weight: normal;
    }
    ul {
      list-style-type: none;
      padding: 0;
    }
    li {
      display: inline-block;
      margin: 0 10px;
    }
    a {
      color: #42b983;
    }
    </style>

    系统二的对应页面也只是把这两个页面弄过去,改下文字方便识别而已。

    写到这里,大家对于这个的思路已经比较清晰了,如果需要优化,我这边建议大家把判断和获取的方法统一弄成控件,然后在router里进行操作,这样会更好。

     这里分享下我封好的控件demo,点击这里查看文章

    环境2:a.aaa.com需要跟b.bbb.com实现跨域,这种不同域名的情况下,想要实现就必须换种方式了.

    这个我还没有写好demo,这个给大家提供我找到的最靠谱的思路及demo

    在这里我将引入第三者,s.sss.com这个站点,就是某个浏览器同时打开了这3个站点,我们访问A站点,先判断自身是否登录,如果session为空,就重定向到S站点,判断S站点上面是否有cookie,如果S站点上面也没有cookie,则由S站点重定向到A站点的登录页.

     这样我们就实现了第一步,S站做的的就是隐藏在幕后,子站先判断自己是否存在session,如果不存在,就重定向到主站S上面去验证.

    第二步,验证登录信息合法性.这里我引入token(令牌),网上有很多资料,描述token的传递,工作方式是这样,A登录成功,保存自身的session,重定向到S,S在自己站点保存一个session跟cookie,session保存token对象{tokenID,userName,startTime,endTime},cookie保存tokenID,tokenID是一个Guid,把token对象缓存在集合里面,另起一个线程,根据endTime(过期时间)来定期清理集合列表,重定向到A的时候再将tokenID传递过去,拿到tokenID后,进入验证环节,S站有提供一个接口,根据tokenID获取token对象,如果获取到对象,且没有失效,则tokenID合法,跳入index页面.情况2,A登录,直接打开B,这时候B自身没有session,会主动请求主站,主站会返回cookieID(S站存在客户端的cookie),这个时候再走验证环节,如果通过,则B根据token对象创建自身的session,再跳入index.

    在这里整个单点登录就已经成功了,接下来附上流程图:

    前面流程图,太丑了,这里补上一张,希望有所帮助.

    收集的demo源码:

    SSO单点登录

    说明:新建2个站点xxx-xxx.com(主站),yyy-yyy.com(子站),修改hosts文件,将这两个域名都指向127.0.0.1即可.

    如果对您有所帮助,欢迎您点个关注,我会定时更新技术文档,大家一起讨论学习,一起进步。

  • 相关阅读:
    python模块--time模块
    python模块--如何相互调用自己写的模块
    Animating Views Using Scenes and Transitions
    fragment 切换
    android textview 设置text 字体
    android intent 5.1
    android EditView ime
    animation of android (4)
    animation of android (3)
    animation of android (2)
  • 原文地址:https://www.cnblogs.com/smileZAZ/p/15095885.html
Copyright © 2011-2022 走看看