基础知识
背景
Token的续期方案有很多,根据前后端自由搭配,自主设计只要没有bug,都没有问题。
我采用的是JWT的过期和续期机制 + Redis锁 + Mysql数据库,前端只需要更换local storage就可以了。
在项目中不考虑性能情况下,我只想简单实现,能颁发token,能续期,能正常过期,能退出登录就可以了。这个方案是遇到了问题,后来就演变成这个方案了。我记录下。
Token续期方案
方案一
- JWT生成token存Mysql,前端存local storage。
登录(邮箱+验证码)
- 前端:邮箱+验证码登录
- 后端:检查邮箱验证码合法性和有效性,JWT生成token,将token存入Mysql。
- 后端返回给前端一个生成的Token,前端存在本地Localstorage中,每次请求API放在Header中。
续期
- 前端携带旧的Token刷新页面, 请求到达后端先做token过期校验,过期返回401,给一个自定义的code码,说明Token过期了。
- 前端校验HttpCode 401,并且Code码为xxx,则请求refresh_token接口。
- refresh_token接口
- 校验这个过期token的maxRefresh(token的刷新时间),
- 如果maxRefresh不在有效期内,返回401,自定义业务状态码xxxx, 踢出登录。
- 如果maxRefresh在有效期内,那么就生成新的token。删除数据库中旧的Token,插入新生成的Token,返回给前端。
- 前端拿到新颁发的Token,存在本地Localstorage中,完成本次续期。
此方案存在的问题
- 重放攻击问题。前端页面一般是多个后端API组合调用的。一般前端调用后台Api是异步的,每次拦截到token过期的业务状态码,都会调用续期,此时后台校验通过后会生成新token,删除旧token,插入新token这一系列操作。每次前端都会反复设置新的token。
方案二
- JWT生成token存Mysql,使用redis做锁,前端存local storage。
续期
- 在方案一的基础上做了改进,增加了redis锁,解决重复刷新token,当setNX失败会返回202状态,告诉前端不需要做任何操作。只有setNX成功之后才能续期。
- 在续期时候检查数据中的token插入时间是不是1分钟之内插入的,如果是在1分钟之内插入的token,则直接返回数据的token。否则删除旧的token插入新生成的token。
- 返回给前端token,前端设置local storage。
解决方案一中的问题
- 通过redis锁限制并发刷新token,并且在1分钟内刷新的token,都返回同一个token。
小结
在使用JWT方案的过程中也是遇到了其他的问题,一一解决后,对jwt理解更清晰了。网上给到的方案也挺多的,我没有一一尝试,大家可以根据具体情况选择合适的方案使用。