JSON Web Token
JWT 特点 体积小,因而传输速度快
传输方式多样,可以通过URL/POST参数/HTTP头部等方式传输
严格的结构化。它自身(在 payload 中)就包含了所有与用户相关的验证消息,如用户可访问路由、访问有效期等信息,服务器无需再去连接数据库验证信息的有效性,并且 payload 支持为你的应用而定制化。
支持跨域验证,可以应用于单点登录。
JWT是Auth0提出的通过对JSON进行加密签名来实现授权验证的方案,编码之后的JWT看起来是这样的一串字符:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ
由 . 分为三段,通过解码可以得到:
- 头部(Header)
// 包括类别(typ)、加密算法(alg);
{
"alg": "HS256",
"typ": "JWT"
}
jwt的头部包含两部分信息:
声明类型,这里是jwt
声明加密的算法 通常直接使用 HMAC SHA256
- 载荷(payload)
载荷(payload) 载荷就是存放有效信息的地方。
- 签名(signature)
签名的目的:签名实际上是对头部以及载荷内容进行签名。所以,如果有人对头部以及载荷的内容解码之后进行修改,再进行编码的话,那么新的头部和载荷的签名和之前的签名就将是不一样的。而且,如果不知道服务器加密的时候用的密钥的话,得出来的签名也一定会是不一样的。 这样就能保证token不会被篡改。
最后,我们将上面拼接完的字符串用HS256算法进行加密。在加密的时候,我们还需要提供一个密钥(secret)。类似盐
这里在第三步我们得到 JWT 之后,需要将JWT存放在 client,之后的每次需要认证的请求都要把JWT发送过来。(请求时可以放到 header 的 Authorization )
一、使用JSON Web Token的好处? 1.性能问题。 JWT方式将用户状态分散到了客户端中,相比于session,可以明显减轻服务端的内存压力。 Session方式存储用户id的最大弊病在于Session是存储在服务器端的,所以需要占用大量服务器内存, 对于较大型应用而言可能还要保存许多的状态,一般还需借助nosql和缓存机制来实现session的存储,如果是分布式应用还需session共享。 2.单点登录。 JWT能轻松的实现单点登录,因为用户的状态已经被传送到了客户端。 token 可保存自定义信息,如用户基本信息,web服务器用key去解析token,就获取到请求用户的信息了。 我们也可以配置它以便包含用户拥有的任何权限。这意味着每个服务不需要与授权服务交互才能授权用户。 3.前后端分离。 以前的传统模式下,后台对应的客户端就是浏览器,就可以使用session+cookies的方式实现登录, 但是在前后分离的情况下,后端只负责通过暴露的RestApi提供数据,而页面的渲染、路由都由前端完成。因为rest是无状态的,因此也就不会有session记录到服务器端。 4.兼容性。 支持移动设备,支持跨程序调用,Cookie 是不允许垮域访问的,而 Token 则不存在这个问题。 5.可拓展性。 jwt是无状态的,特别适用于分布式站点的单点登录(SSO)场景。 比如有3台机器(A、B、C)组成服务器集群,若session存在机器A上,session只能保存在其中一台服务器,此时你便不能访问机器B、C,因为B、C上没有存放该Session, 而使用token就能够验证用户请求合法性,并且我再加几台机器也没事,所以可拓展性好。 6.安全性。因为有签名,所以JWT可以防止被篡改。
JWT是基于token的身份认证的方案。
json web token全称。可以保证安全传输的前提下传送一些基本的信息,以减轻对外部存储的依赖,减少了分布式组件的依赖,减少了硬件的资源。
可实现无状态、分布式的Web应用授权,jwt的安全特性保证了token的不可伪造和不可篡改。
本质上是一个独立的身份验证令牌,可以包含用户标识、用户角色和权限等信息,以及您可以存储任何其他信息(自包含)。任何人都可以轻松读取和解析,并使用密钥来验证真实性。
缺陷: 1)JWT在生成token的时候支持失效时间,但是支持的失效时间是固定的,比如说一天。 但是用户在等出的时候是随机触发的,那么我们jwt token来做这个失效是不可行的,因为jwt在初始化的时候已经定死在什么时候过期了。 采用其他方案,在redis中存储token,设置token的过期时间,每次鉴权的时候都会去延长时间 2)jwt不适合存放大量信息,信息越多token越长
扩展:token 存储介质:https://v3u.cn/a_id_94
Token 认证常见问题以及解决办法
1.注销登录等场景下 token 还有效
与之类似的具体相关场景有:
退出登录; 修改密码; 服务端修改了某个用户具有的权限或者角色; 用户的帐户被删除/暂停。 用户由管理员注销; 这个问题不存在于 Session 认证方式中,因为在 Session 认证方式中,遇到这种情况的话服务端删除对应的 Session 记录即可。但是,使用 token 认证的方式就不好解决了。我们也说过了,token 一旦派发出去,如果后端不增加其他逻辑的话,它在失效之前都是有效的。那么,我们如何解决这个问题呢?查阅了很多资料,总结了下面几种方案:
将 token 存入内存数据库:将 token 存入 DB 中,redis 内存数据库在这里是是不错的选择。如果需要让某个 token 失效就直接从 redis 中删除这个 token 即可。但是,这样会导致每次使用 token 发送请求都要先从 DB 中查询 token 是否存在的步骤,而且违背了 JWT 的无状态原则。 黑名单机制:和上面的方式类似,使用内存数据库比如 redis 维护一个黑名单,如果想让某个 token 失效的话就直接将这个 token 加入到 黑名单 即可。然后,每次使用 token 进行请求的话都会先判断这个 token 是否存在于黑名单中。 修改密钥 (Secret) : 我们为每个用户都创建一个专属密钥,如果我们想让某个 token 失效,我们直接修改对应用户的密钥即可。但是,这样相比于前两种引入内存数据库带来了危害更大,比如:1⃣️如果服务是分布式的,则每次发出新的 token 时都必须在多台机器同步密钥。为此,你需要将必须将机密存储在数据库或其他外部服务中,这样和 Session 认证就没太大区别了。2⃣️ 如果用户同时在两个浏览器打开系统,或者在手机端也打开了系统,如果它从一个地方将账号退出,那么其他地方都要重新进行登录,这是不可取的。 保持令牌的有效期限短并经常轮换 :很简单的一种方式。但是,会导致用户登录状态不会被持久记录,而且需要用户经常登录。 对于修改密码后 token 还有效问题的解决还是比较容易的,说一种我觉得比较好的方式:使用用户的密码的哈希值对 token 进行签名。因此,如果密码更改,则任何先前的令牌将自动无法验证。
token 的续签问题
token 有效期一般都建议设置的不太长,那么 token 过期后如何认证,如何实现动态刷新 token,避免用户经常需要重新登录?
我们先来看看在 Session 认证中一般的做法:假如 session 的有效期30分钟,如果 30 分钟内用户有访问,就把 session 有效期被延长30分钟。
类似于 Session 认证中的做法:这种方案满足于大部分场景。假设服务端给的 token 有效期设置为30分钟,服务端每次进行校验时,如果发现 token 的有效期马上快过期了,服务端就重新生成 token 给客户端。客户端每次请求都检查新旧token,如果不一致,则更新本地的token。这种做法的问题是仅仅在快过期的时候请求才会更新 token ,对客户端不是很友好。 每次请求都返回新 token :这种方案的的思路很简单,但是,很明显,开销会比较大。 token 有效期设置到半夜 :这种方案是一种折衷的方案,保证了大部分用户白天可以正常登录,适用于对安全性要求不高的系统。 用户登录返回两个 token :第一个是 acessToken ,它的过期时间 token 本身的过期时间比如半个小时,另外一个是 refreshToken 它的过期时间更长一点比如为1天。客户端登录后,将 accessToken和refreshToken 保存在本地,每次访问将 accessToken 传给服务端。服务端校验 accessToken 的有效性,如果过期的话,就将 refreshToken 传给服务端。如果有效,服务端就生成新的 accessToken 给客户端。否则,客户端就重新登录即可。该方案的不足是:1⃣️需要客户端来配合;2⃣️用户注销的时候需要同时保证两个 token 都无效;3⃣️重新请求获取 token 的过程中会有短暂 token 不可用的情况(可以通过在客户端设置定时器,当accessToken 快过期的时候,提前去通过 refreshToken 获取新的accessToken)。