优化实战 第 25 期 – 基于Token前端实现SSO单点登录

优化实战 第 25 期 - 基于Token前端实现SSO单点登录

单点登录 (Single Sign On),简称为 SSO,是指在同一账号平台下的多个应用系统中,用户只需要登录一次就可以访问所有相互信任的应用系统

举个例子,系统A系统B 都属于某公司下的两个不同的应用系统,当用户登录 系统A 后,再打开 系统B ,系统便会自动帮用户登录 系统B ,这种现象就属于单点登录

JWT鉴权原理

优化实战 第 25 期 - 基于Token前端实现SSO单点登录

1、客户端使用账号和密码请求登录

2、服务端收到请求,去验证账号与密码

3、验证通过后,服务端会签发一个令牌 Token 并把这个令牌发送给客户端

4、客户端收到令牌将其存储到本地 localStorage

5、客户端每次向服务端请求资源时都需要在 Header Authorization 中携带令牌,允许用户访问该令牌允许的路由、服务和资源

6、服务端收到请求后进行解密并通过秘钥验证令牌,验证通过返回资源,不通过则返回 401 状态码

JWT的介绍

优化实战 第 25 期 - 基于Token前端实现SSO单点登录

JSON Web Token ,简称 JWT ,一种基于 JSON 的认证授权机制,是一个非常轻巧的标准规范。这个规范允许我们在用户和服务器之间传递安全可靠的信息

JWT 也是目前最流行的跨域认证解决方案

JWT的构成

  • 头部(Header)

    {
      "alg": "HS256",
      "typ": "jwt"
    }
    

    通常由令牌的类型 typ 和所使用的签名算法 alg 组成,并使用 Base64URL 进行编码组成 JWT 结构的第一部分

  • 载荷(Payload)

    {
      "exp": Math.floor(Date.now() / 1000 + 60 * 60),  // 设置有效期为 1 个小时
      "uid": "188888"
    }
    

    除了设置过期时间 exp 和用户的 uid ,还可以添加自定义私有字段,使用 Base64URL 进行编码组成 JWT 结构的第二部分

  • 签名(Signature)

    优化实战 第 25 期 - 基于Token前端实现SSO单点登录

    HMACSHA256(base64UrlEncode(Header) + "." + base64UrlEncode(payload), secret)
    

    在服务端设置一个私钥 secret ,使用 Header 指定的算法 HS256 对头部 Header 和 载荷 Payload 进行加密生成签名。

  • 生成 Token

    HeaderPayloadSignature 拼成一个用 英文句号 分隔的字符串,即为 Token

    优化实战 第 25 期 - 基于Token前端实现SSO单点登录

  • 进行验签

    服务端利用签名可以校验 Token 是否被篡改

    优化实战 第 25 期 - 基于Token前端实现SSO单点登录

  • 注意事项

    默认 JWT 是不加密的,所以不要把重要信息放在载荷 Payload 部分

    保存在服务端的 secret 是用来进行 JWT 的签发和验证的,所以在任何场景都不应该泄漏出去。如果客户端得知了这个 secret ,那就意味着客户端可以对 JWT 进行自我签发

JWT的特点

  • 无状态化

    由于服务器或 Session 中不会存储任何用户信息,所以基于 Token 的用户认证是一种服务器无状态的认证方式

    没有会话信息意味着应用程序可以根据需要扩展和添加更多的机器(服务器集群),而不需要考虑用户是在哪一台服务器登录的,适用于 分布式系统

  • 易于扩展

    由于 JWT 存储在应用系统,服务端不进行会话存储,所以便于服务端集群水平扩展

    可以在 JWT 中的载荷 Payload 部分添加自定义的内容用于业务需要

  • 支持跨域

    由于 Token 存储于应用系统,完全由应用系统管理,既减轻了服务端的内存压力也避开了 同源策略 的限制

    这样就可以给任何域名提供 API 服务,不需要担心跨域资源共享问题,即 CORS

  • 防止 CSRF 攻击

    前端向服务端发送请求时,将本地存储的 Token 手动添加到 Authorization 的请求头字段中,这样就避免了 CSRF 漏洞攻击

共享登录状态

  • 实现方案

    前端拿到 Token 后,不仅要将它写入当前域下的 localStorage 中,还要通过 iframe + postMessage() 的方式将它写入多个信任的其他域下的 localStorage 中,从而实现登录状态的共享

    优化实战 第 25 期 - 基于Token前端实现SSO单点登录

  • 实现代码

    Token 写入多个域名下的 localStorage

    const iframe = document.createElement('iframe')
    iframe.src = 'http://www.app.com/static/bridge.html'
    iframe.addEventListener('load', event => {
      iframe.contentWindow.postMessage(token, 'http://www.app.com/static/bridge.html')
    })
    document.body.append(iframe)
    

    iframe 加载的页面中绑定事件监听器,用来接收 Token 数据

    window.addEventListener('message', ({ data, origin, srouce }) => {
      localStorage.setItem('AUTH-TOKEN', data)
    })
    

    在各个应用系统请求的 Header 中携带 Token 令牌

    config.headers.common['Authorization'] = 'Bearer ' + token
    

    说明:Bearer 是 JWT 的认证头部信息

  • 总结

    这种实现方式完全由前端控制,几乎不需要后端参与且支持跨域;出于安全因素考虑应始终使用 originsource 属性验证发信息人的身份

Token 的安全性

  • 安全概览

    为了减少 JWT Token 的泄漏风险,其有效期应该设置得短一些并采用 HTTPS 协议。这样就会存在 JWT Token 过期的情况,导致用户频繁的去登录获取新的 JWT Token,严重影响用户体验

  • 解决方案

    生成 JWT Token 的同时生成 refresh_token,其中 refresh_token 的有效时间长于 JWT Token,当 JWT Token 过期之后,使用 refresh_token 获取新的 JWT Token 与 refresh_token,这样用户就可以享受无感知的刷新体验

    每个 Refresh Token 只能使用一次

    一起学习,加群交流看 沸点

原文链接:https://juejin.cn/post/7086343419626258445 作者:丢丢哥

(0)
上一篇 2023年3月24日 上午11:47
下一篇 2023年3月24日 上午11:58

相关推荐

发表回复

登录后才能评论