JWT⽣成token及过期处理⽅案
## 业务场景
在前后分离场景下,越来越多的项⽬使⽤token作为接⼝的安全机制,APP端或者WEB端(使⽤VUE、REACTJS等构建)使⽤token与后端接⼝交互,以达到安全的⽬的。本⽂结合stackoverflow以及本⾝项⽬实践,试图总结出⼀个通⽤的,可落地的⽅案。
## 基本思路
- 单个token
1. token(A)过期设置为15分钟
2. 前端发起请求,后端验证token(A)是否过期;如果过期,前端发起刷新token请求,后端设置已再次授权标记为true,请求成功
3. 前端发起请求,后端验证再次授权标记,如果已经再次授权,则拒绝刷新token的请求,请求成功
4. 如果前端每隔72⼩时,必须重新登录,后端检查⽤户最后⼀次登录⽇期,如超过72⼩时,则拒绝刷新token的请求,请求失败
- 授权token加上刷新token
⽤户仅登录⼀次,⽤户改变密码,则废除token,重新登录
## 1.0实现
1.登录成功,返回access\_token和refresh\_token,客户端缓存此两种token;
2.使⽤access_token请求接⼝资源,成功则调⽤成功;如果token超时,客户端
携带refresh\_token调⽤中间件接⼝获取新的access\_token;
3.中间件接受刷新token的请求后,检查refresh_token是否过期。
如过期,拒绝刷新,客户端收到该状态后,跳转到登录页;
如未过期,⽣成新的access\_token和refresh\_token并返回给客户端(如有可能,让旧的refresh\_token失效),客户端携带新的
access\_token重新调⽤上⾯的资源接⼝。
4.客户端退出登录或修改密码后,调⽤中间件注销旧的token(使access\_token和refresh\_token失效),同时清空客户端的access\_token和refresh\_toke。
后端表
id user\_id client\_id client\_secret refresh\_token expire\_in create\_date del_flag
## 2.0实现
场景: access\_token访问资源 refresh\_token授权访问 设置固定时间X必须重新登录
1.登录成功,后台jwt⽣成access\_token(jwt有效期30分钟)和refresh\_token(jwt有效期15天),并缓存到redis(hash-key为
token,sub-key为⼿机号,value为设备唯⼀编号(根据⼿机号码,可以⼈⼯废除全部token,也可以根据sub-key,废除部分设备的token。),设置过期时间为1个⽉,保证最终所有token都能删除),返回后,客户端缓存此两种token;
2.使⽤access\_token请求接⼝资源,校验成功且redis中存在该access\_token(未废除)则调⽤成功;如果token超时,中间件删除
access\_token(废除);客户端再次携带refresh\_token调⽤中间件接⼝获取新的access_token;
3.中间件接受刷新token的请求后,检查refresh_token是否过期。
如过期,拒绝刷新,删除refresh_token(废除); 客户端收到该状态后,跳转到登录页;
如未过期,检查缓存中是否有refresh\_token(是否被废除),如果有,则⽣成新的access\_token并返回给客户端,客户端接着携带新的access_token重新调⽤上⾯的资源接⼝。
4.客户端退出登录或修改密码后,调⽤中间件注销旧的token(中间件删除access\_token和refresh\_token(废除)),同时清空客户端侧的access\_token和refresh\_toke。
5.如⼿机丢失,可以根据⼿机号⼈⼯废除指定⽤户设备关联的token。
6.以上3刷新access_token可以增加根据登录时间判断最长X时间必须重新登录,此时则拒绝刷新token。(拒绝的场景:失效,长时间未登录,频繁刷新)
2.0 变动
1.登录
2.登录
3.增加刷新access_token接⼝
4.退出登录
5.修改密码
## 3.0实现
场景:⾃动续期 长时间未使⽤需重新登录
1.登录成功,后台jwt⽣成access\_token(jwt有效期30分钟),并缓存到redis(hash-key为access\_token,sub-key为⼿机号,value为设备唯⼀编号(根据⼿机号码,可以⼈⼯废除全部token),设置access_token过期时间为7天,保证最终所有token都能删除),返回后,客户端缓存此token;
2.使⽤access\_token请求接⼝资源,校验成功且redis中存在该access\_token(未废除)则调⽤成功;如果token超时,中间件删除
access\_token(废除),同时⽣成新的access\_token并返回。客户端收到新的access_token,
再次请求接⼝资源。
3.客户端退出登录或修改密码后,调⽤中间件注销旧的token(中间件删除access\_token(废除)),同时清空客户端侧的access\_token。
4.以上2 可以增加根据登录时间判断最长X时间必须重新登录,此时则拒绝刷新token。(拒绝的场景:长时间未登录,频繁刷新)
5.如⼿机丢失,可以根据⼿机号⼈⼯废除指定⽤户设备关联的token。
3.0 变动
1.登录
2.登录
3.退出登录
4.修改密码
1.3 场景:token过期重新登录 长时间未使⽤需重新登录
1.登录成功,后台jwt⽣成access\_token(jwt有效期7天),并缓存到redis,key为 "user\_id:access\_token",value为access\_token(根
据⽤户id,可以⼈⼯废除指定⽤户全部token),设置缓存过期时间为7天,保证最终所有token都能删除,请求返回后,客户端缓存此access_token;
2.使⽤access\_token请求接⼝资源,校验成功且redis中存在该access\_token(未废除)则调⽤成功;如果token超时,中间件删除access\_token(废除),同时⽣成新的access\_token并返回。客户端收到新的access_token,
再次请求接⼝资源。
3.客户端退出登录或修改密码后,调⽤中间件注销旧的token(中间件删除access\_token(废除)),同时清空客户端侧的access\_token。
4.以上2 可以增加根据登录时间判断最长X时间必须重新登录,此时则拒绝刷新token。(拒绝的场景:长时间未登录,频繁刷新)
5.如⼿机丢失,可以根据⼿机号⼈⼯废除指定⽤户设备关联的token。
1.3 变动
1.登录
2.登录
3.退出登录
4.修改密码
# 解决⽅案
2.0 场景: access\_token访问资源 refresh\_token授权访问 设置固定时间X必须重新登录
1.登录成功,后台jwt⽣成access\_token(jwt有效期30分钟)和refresh\_token(jwt有效期15天),并缓
存到redis(hash-key为token,sub-key为⼿机号,value为设备唯⼀编号(根据⼿机号码,可以⼈⼯废除全
部token,也可以根据sub-key,废除部分设备的token。),设置过期时间为1个⽉,保证最终所有token都
能删除),返回后,客户端缓存此两种token;
2.使⽤access\_token请求接⼝资源,校验成功且redis中存在该access\_token(未废除)则调⽤成功;如果
token超时,中间件删除access\_token(废除);客户端再次携带refresh\_token调⽤中间件接⼝获取新的
access_token;
3.中间件接受刷新token的请求后,检查refresh_token是否过期。
如过期,拒绝刷新,删除refresh_token(废除); 客户端收到该状态后,跳转到登录页;
如未过期,检查缓存中是否有refresh\_token(是否被废除),如果有,则⽣成新的access\_token并返回给
客户端,客户端接着携带新的access_token重新调⽤上⾯的资源接⼝。
4.客户端退出登录或修改密码后,调⽤中间件注销旧的token(中间件删除access\_token和refresh\_token(
废除)),同时清空客户端侧的access\_token和refresh\_toke。
5.如⼿机丢失,可以根据⼿机号⼈⼯废除指定⽤户设备关联的token。
6.以上3刷新access_token可以增加根据登录时间判断最长X时间必须重新登录,此时则拒绝刷新token。(
拒绝的场景:失效,长时间未登录,频繁刷新)
2.0 变动
1.登录
js获取json的key和value2.登录
3.增加刷新access_token接⼝
4.退出登录
5.修改密码
3.0 场景:⾃动续期 长时间未使⽤需重新登录
1.登录成功,后台jwt⽣成access_token(jwt有效期30分钟),并缓存到redis(hash-key为
access_token,sub-key为⼿机号,value为设备唯⼀编号(根据⼿机号码,可以⼈⼯废除全部token,也可以
根据sub-key,废除部分设备的token。),设置access_token过期时间为1个⽉,保证最终所有token都能删除),返回后,客户端缓存此token;
2.使⽤access\_token请求接⼝资源,校验成功且redis中存在该access\_token(未废除)则调⽤成功;如果token超时,中间件删除access\_token(废除),同时⽣成新的access\_token并返回。客户端收到新的access_token,
再次请求接⼝资源。
3.客户端退出登录或修改密码后,调⽤中间件注销旧的token(中间件删除access_token(废除)),同时清
空客户端侧的access_token。
4.以上2 可以增加根据登录时间判断最长X时间必须重新登录,此时则拒绝刷新token。(拒绝的场景:长
时间未登录,频繁刷新)
5.如⼿机丢失,可以根据⼿机号⼈⼯废除指定⽤户设备关联的token。
3.0 变动
1.登录
2.登录
3.退出登录
4.修改密码
4.0 场景:token过期重新登录 长时间未使⽤需重新登录
1.登录成功,后台jwt⽣成access_token(jwt有效期7天),并缓存到redis,key为
"user\_id:access\_token" + ⽤户id,value为access_token(根据⽤户id,可以⼈⼯废除指定⽤户全部
token),设置缓存过期时间为7天,保证最终所有token都能删除,请求返回后,客户端缓存此
access_token;
2.使⽤access\_token请求接⼝资源,校验成功且redis中存在该access\_token(未废除)则调⽤成功;如果
token超时,中间件删除access\_token(废除),同时⽣成新的access\_token并返回。客户端收到新的
access_token,
再次请求接⼝资源。
3.客户端退出登录或修改密码后,调⽤中间件注销旧的token(中间件删除access_token(废除)),同时清
空客户端侧的access_token。
4.以上2 可以增加根据登录时间判断最长X时间必须重新登录,此时则拒绝刷新token。(拒绝的场景:长
时间未登录,频繁刷新)
5.如⼿机丢失,可以根据⼿机号⼈⼯废除指定⽤户设备关联的token。
4.0 变动
1.登录
2.登录
3.退出登录
4.修改密码
## 最终实现
### 后端
1. 在登录接⼝中 如果校验账号密码成功 则根据⽤户id和⽤户类型创建jwt token(有效期设置为-1,即永不过期),得到A
2. 更新登录⽇期(当前时间new Date()即可)(业务上可选),得到B
3. 在redis中缓存key为ACCESS_TOKEN:userId:A(加上A是为了防⽌⽤户多个客户端登录 造成token覆盖),value为B的毫秒数(转换成字符串类型),过期时间为7天(7 * 24 * 60 * 60)
4. 在登录结果中返回json格式为{"result":"success","token": A}
5. ⽤户在接⼝请求header中携带token进⾏登录,后端在所有接⼝前置进⾏拦截,作⽤是解析token 拿到userId和⽤户类型(⽤户调⽤业务接⼝只需要传token即可),
如果解析失败(抛出SignatureException),则返回json(code = 0 ,info= Token验证不通过, errorCode = '1001');
此外如果解析成功,验证redis中key为ACCESS_TOKEN:userId:A 是否存在 如果不存在 则返回json(code = 0 ,info= 会话过期请重新登录, errorCode = '1002');
如果缓存key存在,则⾃动续7天超时时间(value不变),实现频繁登录⽤户免登陆。
6. 把userId和⽤户类型放⼊request参数中 接⼝⽅法中可以直接拿到登录⽤户信息
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论