JSON Web Token(JWT)是一个非常轻巧的规范。这个规范允许我们使用JWT在用户和服务器之间传递安全可靠的信息。
JWT
JWT有3个组成部分,每部分通过点号来分割 header.payload.signature
- 头部(header) 是一个 JSON 对象,描述 JWT 的元数据。
- Jwt的头部是一个JSON,然后使用Base64URL编码,承载两部分信息:
- 声明类型typ,表示这个令牌(token)的类型(type),JWT令牌统一写为JWT
- 声明加密的算法alg,通常直接使用HMACSHA256,即HS256
- 例如:
{ "alg": "HS256", "typ": "JWT"}
- Jwt的头部是一个JSON,然后使用Base64URL编码,承载两部分信息:
- 载荷(payload) 是一个 JSON 对象,用来存放实际需要传递的数据
- payload也是一个JSON字符串,是承载消息具体内容的地方,也需要使用Base64URL编码,payload中可以包含预定义的7个key,它们不是强制性的,但推荐使用,也可以添加任意自定义的key
- iss(issuer): jwt签发者
- sub(subject): jwt所面向的用户
- aud(audience): 接收jwt的一方, 受众
- exp(expiration time): jwt的过期时间,这个过期时间必须要大于签发时间
- nbf(Not Before): 生效时间,定义在什么时间之前.
- iat(Issued At): jwt的签发时间
- jti(JWT ID): jwt的唯一身份标识,主要用来作为一次性token,从而回避重放攻击。
- 例如:
{"sub": "1234567890", "name": "John Doe", "iat": 1516239022}
,该token签发给1234567890,姓名为John Doe(自定义的字段),签发时间为1516239022
- payload也是一个JSON字符串,是承载消息具体内容的地方,也需要使用Base64URL编码,payload中可以包含预定义的7个key,它们不是强制性的,但推荐使用,也可以添加任意自定义的key
- 签证(signature) 对header和payload使用密钥进行签名,防止数据篡改。
- Signature 部分是对前两部分的签名,防止数据篡改。
- 用于签名的密钥是保存在服务器端的,jwt的签发生成也是在服务器端的,密钥secret就是用来进行jwt的签发和jwt的验证,所以,它就是你服务端的私钥,在任何场景都不应该流露出去。
- 注意客户端发送的Authorization HTTP HEADER格式是 “Bearer YOUR_JWT_TOKEN”,这是OAuth的规范规定的。(加前缀Bearer,空格隔开)
- jwt.io调试器
- Base64编码可用于在HTTP环境下传递较长的标识信息。
优点
优点:
- 多样化的传输方式,可以通过URL传输、POST传输、请求头Header传输(常用)
- 简单方便,服务端拿到jwt后无需再次查询数据库校验token可用性,也无需进行redis缓存校验(对jwt中的头部和载荷进行签名,与jwt中的签证进行对比)
- 在分布式系统中,很好地解决了单点登录问题
- 很方便的解决了跨域授权问题,因为跨域无法共享cookie
缺点
缺点:
- 无法在使用过程中废止某个 token,或者更改 token 的权限。也就是说,一旦 JWT 签发了,在到期之前就会始终有效,除非服务器部署额外的逻辑。
- JWT 本身包含了认证信息,一旦泄露,任何人都可以获得该令牌的所有权限。为了减少盗用,JWT 的有效期应该设置得比较短。对于一些比较重要的权限,使用时应该再次对用户进行认证。
- 为了减少盗用,JWT 不应该使用 HTTP 协议明码传输,要使用 HTTPS 协议传输。
- 在退出登录/修改密码时怎样实现JWT Token失效
- 将 token 存入 DB(如 Redis)中,失效则删除;但增加了一个每次校验时候都要先从 DB 中查询 token 是否存在的步骤,而且违背了 JWT 的无状态原则(这不就和 session 一样了么?)。
- 维护一个 token 黑名单,失效则加入黑名单中。
- 在 JWT 中增加一个版本号字段,失效则改变该版本号。
- 在服务端设置加密的 key 时,为每个用户生成唯一的 key,失效则改变该 key。
SpringBoot整合
SpringSecurity整合
注意事项
为什么用JWT?
- JWT只通过算法实现对Token合法性的验证,不依赖数据库,Redis等存储系统,因此可以做到跨服务器验证,只要密钥和算法相同,不同服务器程序生成的Token可以互相验证。
JWT Token需要持久化在Redis中吗?
- 不应该这样做,这样就背离了JWT通过算法验证的初心。
在退出登录时怎样实现JWT Token失效呢?
- 退出登录,只要客户端端把Token丢弃就可以了,服务器端不需要废弃Token。
怎样保持客户端长时间保持登录状态?
- 服务器端提供刷新Token的接口,客户端负责按一定的逻辑刷新服务器Token。
服务器端是否应该从JWT中取出userid用于业务查询?
- REST API是无状态的,意味着服务器端每次请求都是独立的,即不依赖以前请求的结果,因此也不应该依赖JWT token做业务查询, 应该在请求报文中单独加个userid 字段。(如果用户token泄露的话,只从jwt token里取就可能导致越权)
- 为了做用户水平越权的检查,可以在业务层判断传入的userid和从JWT token中解析出的userid是否一致, 有些业务可能会允许查不同用户的数据(那就要规定好当前用户id和待查询用户id所使用的字段名,不要冲突了)。
参考资料: