黑马程序员技术交流社区

标题: 【上海校区】jwt 实践以及与 session 对比 [打印本页]

作者: 不二晨    时间: 2018-7-23 09:56
标题: 【上海校区】jwt 实践以及与 session 对比
Json Web Token 是 rfc7519 出的一份标准,使用 JSON 来传递数据,用于判定用户是否登录状态。
jwt 之前,使用 session 来做用户认证。
以下代码均使用 javascript 编写。
session传统登录的方式是使用 session + token。
session 是指在服务器端使用 redis 或者 sql 类数据库,存储 user_id 以及 token 的键值对关系,基本工作原理如下。
const sessions = {  "ABCED1": 10086,  "CDEFA0": 10010}// 通过 token 获取 user_id, 完成认证过程function getUserIdByToken (token) {  return sessions[user_id]}复制代码token 是指在客户端使用 token 作为用户状态凭证,浏览器一般存储在 localStorage 或者 cookie 中。
如果存储在 cookie 中就是经常听到的 session + cookie 的登录方案。其实存储在 cookie,localStorage 甚至 IndexedDB 或者 WebSQL 各有利弊,核心思想一致。
关于 cookie 以及 token 优缺点,在 token authetication vs cookies 中有讨论。
如果不使用 cookie,可以采取 localStorage + Authorization 的方式进行认证。
// http 的头,每次请求权限接口时,需要携带 Authorization Headerconst headers = {  Authorization: `Bearer: ${localStorage.get('token')}`}复制代码
推荐一个库 localForage,使用 IndexedDB,WebSQL 以及 IndexedDB 做键值对存储。
无状态登录session 需要在数据库中保持用户及token对应信息,所以叫 有状态
试想一下,如何在数据库中不保持用户状态也可以登录。
第一种方法: 前端直接传 user_id 给服务端
缺点也特别特别明显,容易被用户篡改成任务 user_id,权限设置形同虚设。不过思路正确,接着往下走。
改进: 对 user_id 进行对称加密
比上边略微强点,如果说上一种方法是空窗户,这种方法就是糊了纸的窗户。
改进: 对 user_id 不需要加密,只需要进行签名,保证不被篡改
这便是 jwt 的思想,user_id,加密算法和签名一起存储到客户端,每次请求接口时,服务器判断签名是否一致。
Json Web Tokenjwt 由 Header,Payload 以及 Signature 由 . 拼接而成。
HeaderHeader 由非对称加密算法和类型组成,如下
const header = {  // 加密算法  alg: 'HS256',  type: 'jwt'}复制代码PayloadPayload 中由 Registered Claim 以及需要通信的数据组成。这些数据字段也叫 Claim。
Registered Claim 中比较重要的是 "exp" Claim 表示过期时间,在用户登录时会设置过期时间。
const payload = {  // 表示 jwt 创建时间  iat: 1532135735,  // 表示 jwt 过期时间  exp: 1532136735,  // 用户 id,用以通信  user_id: 10086}复制代码SignatureSign 由 Header,Payload 以及 secretOrPrivateKey 计算而成。
对于 secretOrPrivateKey,如果加密算法采用 HMAC,则为字符串,如果采用 RSA 或者 ECDSA,则为 PrivateKey。
// 由 HMACSHA256 算法进行签名,secret 不能外泄const sign = HMACSHA256(base64.encode(header) + '.' + base64.encode(payload), secret)// jwt 由三部分拼接而成const jwt = base64.encode(header) + '.' + base64.encode(payload) + '.' + sign复制代码
从生成 jwt 规则可知客户端可以解析出 payload,因此不要在 payload 中携带敏感数据,比如用户密码
校验在生成规则中可知,jwt 前两部分是对 header 以及 payload 的 base64 编码。
当服务器收到客户端的 token 后,解析前两部分得到 header 以及 payload,并使用 header 中的算法与 secretOrPrivateKey 进行签名,判断与 jwt 中的签名是否一致。
如何判断 token 过期?
验证码jwt 不仅可以用在用户认证,也可以用来校验验证码。
可以把验证码的结果字符串作为 secret。
const jwt = require('jsonwebtoken')// 假设验证码为字符验证码,字符为 ACDEconst token = jwt.sign({}, 'ACDE')复制代码无状态 VS 有状态关于无状态和有状态,在其它技术方向也有对比,比如 React 的 stateLess component 以及 stateful component,函数式编程中的副作用可以理解为状态,http 也是一个无状态协议,需要靠 header 以及 cookie 携带状态。
在用户认证这里,有无状态是指是否依赖外部数据存储,如 mysql,redis 等。
思考以下几个关于登录的问题如何使用 session 以及 jwt 实现
当用户注销时,如何使该 token 失效因为 jwt 无状态,不保存用户设备信息,没法单纯使用它完成以上问题,可以再利用数据库保存一些状态完成。
如何允许用户只能在一个设备登录,如微信对于这个需求,session 稍微简单些,毕竟 jwt 也需要依赖数据库。
如何允许用户只能在最近五个设备登录,如诸多播放器对于这个需求,jwt 略简单些,而使用 session 还需要多维护一张 token 表。
如何使某一用户踢掉除现有设备外的其它所有设备,如诸多播放器如何显示该用户登录设备列表总结从以上问题得知,如果不需要控制登录设备数量以及设备信息,无状态的 jwt 是一个不错的选择。一旦涉及到了设备信息,就需要对 jwt 添加额外的状态支持,增加了认证的复杂度,此时选用 session 是一个不错的选择。
jwt 不是万能的,是否采用 jwt,需要根据业务需求来确定。


作者:shanyue
链接:https://juejin.im/post/5b532492e51d455d6825c0cc




作者: 摩西摩西OvO    时间: 2018-7-23 13:30

作者: 不二晨    时间: 2018-7-23 13:48
奈斯,优秀
作者: 摩西摩西OvO    时间: 2018-7-26 09:11

作者: 吴琼老师    时间: 2018-7-26 15:33





欢迎光临 黑马程序员技术交流社区 (http://bbs.itheima.com/) 黑马程序员IT技术论坛 X3.2