单点登录 (Single Sign On),简称为 SSO
,是指在同一账号平台下的多个应用系统中,用户只需要登录一次就可以访问所有相互信任的应用系统
举个例子,系统A
和 系统B
都属于某公司下的两个不同的应用系统,当用户登录 系统A
后,再打开 系统B
,系统便会自动帮用户登录 系统B
,这种现象就属于单点登录
JWT鉴权原理
1、客户端使用账号和密码请求登录
2、服务端收到请求,去验证账号与密码
3、验证通过后,服务端会签发一个令牌 Token
并把这个令牌发送给客户端
4、客户端收到令牌将其存储到本地 localStorage
中
5、客户端每次向服务端请求资源时都需要在 Header Authorization
中携带令牌,允许用户访问该令牌允许的路由、服务和资源
6、服务端收到请求后进行解密并通过秘钥验证令牌,验证通过返回资源,不通过则返回 401
状态码
JWT的介绍
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)
HMACSHA256(base64UrlEncode(Header) + "." + base64UrlEncode(payload), secret)
在服务端设置一个私钥
secret
,使用Header
指定的算法HS256
对头部Header
和 载荷Payload
进行加密生成签名。 -
生成 Token
把
Header
、Payload
、Signature
拼成一个用英文句号
分隔的字符串,即为Token
-
进行验签
服务端利用签名可以校验
Token
是否被篡改 -
注意事项
默认
JWT
是不加密的,所以不要把重要信息放在载荷Payload
部分保存在服务端的
secret
是用来进行JWT
的签发和验证的,所以在任何场景都不应该泄漏出去。如果客户端得知了这个secret
,那就意味着客户端可以对JWT
进行自我签发
JWT的特点
-
无状态化
由于服务器或
Session
中不会存储任何用户信息,所以基于Token
的用户认证是一种服务器无状态的认证方式没有会话信息意味着应用程序可以根据需要扩展和添加更多的机器(服务器集群),而不需要考虑用户是在哪一台服务器登录的,适用于
分布式系统
-
易于扩展
由于
JWT
存储在应用系统,服务端不进行会话存储,所以便于服务端集群水平扩展可以在
JWT
中的载荷Payload
部分添加自定义的内容用于业务需要 -
支持跨域
由于
Token
存储于应用系统,完全由应用系统管理,既减轻了服务端的内存压力也避开了同源策略
的限制这样就可以给任何域名提供
API
服务,不需要担心跨域资源共享问题,即CORS
-
防止 CSRF 攻击
前端向服务端发送请求时,将本地存储的
Token
手动添加到Authorization
的请求头字段中,这样就避免了CSRF
漏洞攻击
共享登录状态
-
实现方案
前端拿到
Token
后,不仅要将它写入当前域下的localStorage
中,还要通过iframe + postMessage()
的方式将它写入多个信任的其他域下的localStorage
中,从而实现登录状态的共享 -
实现代码
将
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 的认证头部信息
-
总结
这种实现方式完全由前端控制,几乎不需要后端参与且支持跨域;出于安全因素考虑应始终使用
origin
和source
属性验证发信息人的身份
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 作者:丢丢哥