一般情况下, 客户的会话数据会存在文件中, 或者引入 Redis 来存储, 实现 session 的管理, 但是这样操作会存在一些问题, 使用文件来存储的时候, 在多台机器上, 比较难实现共享, 使用 Redis 来存储的时候, 则需要引入多一个集群, 这样会增加管理的工作量, 也不方便. 有一个直观的办法, 就是将 session 数据, 存储在客户端中, 使用签名校验数据是否有篡改, 客户请求的时候, 把 session 数据带上, 获取里面的数据, 通过校验, 然后进行身份认证.
数据存储在客户端中, 会存在一些挑战:
数据安全问题
数据量不能太大
续签的问题
注销的问题
为了实现客户端存储会话数据的解决方案, 制定了 JSON web Token 的协议, 详细的协议可以在: RFC7529 https://tools.ietf.org/html/rfc7519 查看. 下面我们看看 jwt 协议是怎样解决上面的挑战的.
JWT 的结构:
JWT 加密后, 使用的格式, 分为三部分, header,payload 和 signature, 使用. 号连接起来:
Header.Payload.Signature
JWT 的 header:
JWT 的 header, 定义了存储的算法和协议名称:
- {
- "alg": "HS256",
- "typ": "JWT"
- }
JWT 的 Payload:
下面这些负载字段, 是 JWT 协议提供选用, 一般情况下, payload 的数据是不加密存储在客户端中的, 所以要注意不要存储敏感信息:
iss (issuer): 签发人
exp (expiration time): 过期时间
sub (subject): 主题
aud (audience): 受众
nbf (Not Before): 生效时间
iat (Issued At): 签发时间
jti (JWT ID): 编号
payload 除了这些字段, 还可以扩展一些数据, 更加符合我们的需求:
- {
- "iss": "foo",
- "extend_data": "hell"
- }
JWT 的 Signature
服务端, 有一个秘钥, 通过秘钥对 header 和 payload 进行签名, 使用 header 中指定的签名算法类型, 一般有 HMAC,RSA 和 ECDSA, 下面是签名的格式:
HMACSHA256(base64UrlEncode(header) + "." + base64UrlEncode(payload), secret)
JWT 的通讯方式
JWT 一般会将 token 数据存储在 http 请求的 header 中, 通过 Bearer 来分隔:
- headers: {
- 'Authorization': 'Bearer' + token
- }
定义好数据结构和通讯方式, 下面看看如何处理一些问题:
续签问题
每一个 token 产生, 都应该限制好过期时间, 确保只能在一段时间内有效, 保证安全. 当达到过期时间时, 需要对 token 进行续签, 可以定时想服务器提交请求, 重新获取 token 来实现.
注销问题
当客户登录的时候, 需要注销登录会话, 由于 token 是没有状态的, 只能在客户端把 token 删除, 伪造一个注销的状态, 真正的注销只能等待 token 过期.
也可以有种办法, 就是把 token 的信息记录在 Redis 中, 当客户退出时, 讲 Redis 中的 token 删除, 而一般请求时, 会通过 Redis 对数据进行校验, 这样可以实现真的注销效果, 但要引入多一个组件, 把 token 变为有状态, 如果用这种办法, 也就不符合 token 存储在客户端的模式了
总结
如果能够支持, 会话用的数据量较小, 对注销可以等待超时的长效的场景, 使用 jwt 作为会话数据存储是会比较方便的. 而对于会话数据量大的场景, 还是使用一般的方式比较好点.
参考资料
RFC7529 https://tools.ietf.org/html/rfc7519
来源: http://www.bubuko.com/infodetail-2949067.html