JWT有状态登陆与无状态登陆
单点登录与JWT
JWT
全称: Json Web Token 。
作用: JWT 的作用是 用户授权(Authorization) ,而不是用户的身份认证(Authentication) 。
用户认证 指的是使用用户名、密码来验证当前用户的身份,即用户登录。
用户授权 指用户登录成功后,当前用户有足够的权限访问特定的资源。
传统的
Session
登录:
- 用户登录后,服务器会保存登录的
Session
信息Session ID
会通过cookie
传递给前端http
请求会附带cookie
这种登录方式称为 有状态登录 。
一、
1、有状态登录(Session):
传统上,我们会使用 Session 和 Cookie 来保存用户的授权信息。第一步,登录过程,用户使用用户名和密码来登录系统,服务器会来验证用户名和密码是否正确,如果正确,服务器会给这个用户创建一个包含用户登录信息、角色、权限的一个叫做 Session 的东西,然后把这个 Session 保存起来,同时把这个 Session 的 ID 以 Cookie 的形式发送给前端,表示用户验证成功,登录完成了;接下来如果用户希望访问某些资源,前端要向后端发起一个 HTTP 的请求,同时相应的 Cookie 也会跟随请求一起发送给服务器,而服务器取得 Cookie 以后就会去查找是否有 Session ID ,然后通过 Session ID 提取相应的 Session 来确定用户的身份与权限,如果 Session 与 ID 相符,同时用户的信息也能提供相应的权限,服务器就会认为这个用户已经登录了,随后资源信息就会通过 HTTP 响应给前端。
客户端 服务器 POST api/login{email,password} 通过 cookie 传递 session ID 请求携带 cookie ,并包含 sessionID 如果用户认证成功,则返回资源 用户登陆 访问资源 创建并保存 session 通过sessionID 来确定用户 身份与权限 有状态登陆(Session)
2、无状态登陆(JWT):
第一步,同样是登陆,用户使用用户名和密码登陆,如果登陆成功,服务器就会返回一个加密文档,这个文档就是 JWT ,其中包含用户密码以外,全部的认证信息,包括用户名、Email、角色、权限等等,而前端在拿到 这个JWT 以后就可以把它保存起来了,可以保存到 Cookie 中,也可以保存到浏览器的 LocaStorage 里面,而生成的 JWT 不需要在后端保存,接下来第二步,用户如果需要访问某些权限的时候,这时候,用户就要把 JWT 放在 HTTP 请求 herder 中与请求一起发送给服务器,服务器取得 JWT 以后 会使用私钥给 JWT 文档解密 ,如果解密成功而且数据依然有效则代表用户已经登陆了,如果 JWT 所描述的用户权限允许该用户访问资源,那么服务器就会把资源的信息,通过 HTTP 响应发回给前端。
客户端 服务器 POST api/login{email,password} 返回 JWT 请求携带 JWT 如果 JWT 有效则返回资源 用户登陆 访问资源 使用密码加密 JWT 使用相同的密码验证JWT 无状态登陆(JWT)
3、区别与差异:
传统上用户登陆状态会以 Session 的形式保存在服务器上,而 Session ID 则保存在前端的 Cookie 中;而使用 JWT 以后,用户的认证信息将会以 Token 的形式保存在前端,服务器不需要保存任何的用户状态,这也就是为什么 JWT 被称为无状态登陆的原因,无状态登陆最大的优势就是完美支持分布式部署,可以使用一个 Token 发送给不同的服务器,而所有的服务器都会返回同样的结果。
二、
1、有状态登陆
这样做的缺点是什么?
服务端保存大量数据增加服务端的压力
服务端保存用户状态,无法进行水平扩展(集群)→针对Session来说
客户端请求依赖服务端,多次请求必须访问同一台服务器→针对Session来说
(如果域名不同那么 Session域很难进行处理)
2、无状态登陆
无状态登陆的流程:
- 当客户端第一次请求服务时,服务端对用户信息进行信息认证(登陆)
- 认证通过,将用户信息进行加密形成token,返回客户端,作为登陆凭证
- 以后每次请求,客户端都携带认证的tocken
- 服务端对tocken进行解密,判断是否存在
服务器不保存用户的登录信息!
微服务集群中的每个服务,对外提供的都是Rest风格的接口。而Rest风格的一个最重要的规范就是:服务的无状态性,即:
- 服务端不保存任何客户端请求者信息
- 客户端的每次请求必须具备自描述信息(jwt),通过这些信息识别客户端身份
带来的好处是什么呢?
-
客户端请求不依赖服务端的信息,任何多次请求不需要必须访问到同一台服务
-
服务端的集群和状态对客户端透明
-
服务端可以任意的迁移和伸缩
-
减小服务端存储压力
JWT官网: https://jwt.io/
JWT交互流程:
- 授权流程:
- 1、用户请求登录,携带用户名密码到授权中心
- 2、授权中心携带用户名密码,到用户中心查询用户
- 3、查询如果正确,生成JWT凭证
- 4、返回JWT给用户
- 鉴权流程:
- 1、用户请求某微服务功能,携带JWT
- 2、微服务将jwt交给授权中心校验
- 3、授权中心返回校验结果到微服务
- 4、微服务判断校验结果,成功或失败
- 5、失败则直接返回401
- 6、成功则处理业务并返回
因为JWT签发的token中已经包含了用户的身份信息,并且每次请求都会携带,这样服务的就无需保存用户信息,甚至无需去数据库查询,完全符合了Rest的无状态规范。
不过,这个过程是不是就完美了呢?
可以发现,用户访问我们的网站,一次授权后,以后访问微服务都需要鉴权,那么每次鉴权都需要访问授权中心,一个用户请求,被分解为2次请求才能完成,效率比较低。
能不能直接在微服务的完成鉴权,不去找授权中心呢?
如果这样,就可以减少一次网络请求,效率提高了一倍。但是,微服务并没有鉴定JWT的能力
,因为鉴定需要通过JWT来完成。我们不能把JWT交给其它微服务,存在安全风险。
怎么办?
这就要用到RSA非对称加密技术了。
三、非对称加密验签
1、加密技术的类型
加密技术是对信息进行编码和解码的技术,编码是把原来可读信息(又称明文)译成代码形式(又称密文),其逆过程就是解码(解密),加密技术的要点是加密算法,加密算法可以分为三类:
- 对称加密,如AES
- 基本原理:将明文分成N个组,然后使用**对各个组进行加密,形成各自的密文,最后把所有的分组密文进行合并,形成最终的密文。
- 优势:算法公开、计算量小、加密速度快、加密效率高
- 缺陷:双方都使用同样**,安全性得不到保证
- 非对称加密,如RSA
- 基本原理:同时生成两把**:私钥和公钥,私钥隐秘保存,公钥可以下发给信任客户端
- 私钥加密,持有私钥或公钥才可以解密
- 公钥加密,持有私钥才可解密
- 优点:安全,难以**
- 缺点:算法比较耗时
- 基本原理:同时生成两把**:私钥和公钥,私钥隐秘保存,公钥可以下发给信任客户端
- 不可逆加密,如MD5,SHA
- 基本原理:加密过程中不需要使用**,输入明文后由系统直接经过加密算法处理成密文,这种加密后的数据是无法被解密的,无法根据密文推算出明文。
2、非对称加密验签
有了非对称加密,我们就可以改变签名和验签的方式了:
-
生成RSA**对,私钥存放在授权中心,公钥下发给微服务
-
在授权中心利用私钥对JWT签名
-
在微服务利用公钥验证签名有效性
因为非对称加密的特性,不用担心公钥泄漏问题,因为公钥是无法伪造签名的,但要确保私钥的安全和隐秘。
非对称加密后的授权和鉴权流程:
授权中心的主要职责:
- 用户登录鉴权:
- 接收用户的登录请求,
- 通过用户中心的接口校验用户名密码
- 使用私钥生成JWT并返回
- 用户登录状态校验
- 判断用户是否登录,其实就是token的校验
- 用户登出
- 用户选择退出登录后,要让token失效
- 用户登录状态刷新
- 用户登录一段时间后,JWT可能过期,需要刷新有效期
我们把jwt返回到客户端,客户端保存到哪里呢?
列举两种解决方案:(还可以用redis)
- 方案一:存入web存储如:LocalStorage或SessionStorage中
- 优点:
- 不用担心cookie禁用问题
- 不会随着浏览器自动发送,可以减少不必要的请求头大小
- 缺点:
- 不会随着浏览器自动发送,需要前段额外代码,携带jwt
- 会遭到XSS(跨站脚本)攻击
- 优点:
- 方案二:存入cookie
- 优点:
- 会随着浏览器自动发送,客户端不用任何额外代码
- 使用httponly,避免XSS攻击风险
- 缺点:
- 会随着浏览器自动发送,某些时候有些多余
- 可能遭到CSRF(跨站资源访问)攻击
- 优点:
这里我们采用哪一种呢?
我们采用cookie方案,cookie方案的两个缺陷我们也可以解决:
- 问题1:会随着浏览器自动发送,某些时候有些多余
- 解决:后端服务与其它服务资源(如静态资源)采用不同域名,浏览器的同源策略会限制cookie
- 问题2:可能遭到CSRF(跨站资源访问)攻击
- 解决:避免get请求操作服务器资源,遵循Rest风格,必要时在token中存入随机码
JWT内部设置了token的有效期,默认是30分钟,30分钟后用户的登录信息就无效了,用户需要重新登录,用户体验不好,怎么解决呢?
JWT的缺点就凸显出来了:
- JWT是生成后无法更改,因此我们无法修改token中的有效期,也就是无法续签。
怎么办?
3种解决方案:
- 方案1:每次用户访问网站,都重新生成token。操作简单粗暴,但是token写入频率过高,效率实在不好。
- 方案2:登录时,除了生成jwt,还另外生成一个刷新token,每当token过期,如果用户持有刷新token,则为其重新生成一个token。这种方式比较麻烦,而且会增加header大小。
- 方案3:cookie即将到期时,重新生成一个token。比如token有效期为30分钟,当用户请求我们时,我们可以判断如果用户的token有效期还剩下15分钟,那么就重新生成token,可以看做上面两种的折中方案。
来源:
https://www.freesion.com/article/4184756009/
https://blog.csdn.net/weixin_45581482/article/details/119336595
https://blog.csdn.net/fhkkkbfgggjk/article/details/85047461