博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
token超时刷新策略
阅读量:2169 次
发布时间:2019-05-01

本文共 7419 字,大约阅读时间需要 24 分钟。

需求分析

我们在做用户中心时,对于登录用户签发其对应的token,对token设置他的固定有效期时间。我们通常会遇到一个这样的问题:在有效期内用户携带token访问没问题,当过了有效期后token失效,用户需要重新登录获取新的token。当token时间设置得很短,假如只有10分钟,那么用户当问期间每隔10分钟就要重新登录一次,这样对于用户来说是比较差的体验。

一个App鉴权流程(token刷新机制):

1.活跃的用户应该在无感知的情况下在token失效后获取到新的token,携带这个新的token进行访问,2.不活跃的用户在token失效后需要重新进行登录认证。。

设计思路

用户登录时,为登录用户签发JWT(Json Web Token),并设置JWT的有效时间,同时将该用户的JWT保存至redis缓存中,并设置缓存的有效期,缓存中保存的JWT的有效期要大于JWT本身的有效时间。其中:缓存时间-JWT有效时间=token可刷新时间

token超时刷新策略

Token登录流程

用户进行接口请求时,需要携带token,而此时进行接口请求时通常有以下几种状态:

  1. 未携带token传入:返回token为空,权限不足;
  2. 错误token传入:返回token错误,权限不足;
  3. 正常token传入:通过 。
  4. 正常token但已过期:查询redis中该token是否存在,如果缓存中存在则刷新token,并将刷新后的token重新放入redis,使用刷新后的token请求资源; 缓存中不存在,则token刷新期限已过,用户需重新登录。

登陆代码

public ResultDTO login(String name, String password) {        Map
tokenMap = new HashMap<>(); String token = null; try { User user = userMapper.findByUsername(name); //查询用户登录的账号是否存在 if(user == null){ return ResultDTO.failure(new ResultError(UserError.EMP_IS_NULL_EXIT)); }else{ //校验用户密码是否正确 if(!user.getPassword().equals(password)){ return ResultDTO.failure(new ResultError(UserError.PASSWORD_OR_NAME_IS_ERROR)); }else { // 获取token,将 user id 、userName保存到 token 里面 token = JwtUtil.sign(user.getUsername(),user.getId(),user.getPassword()); //将用户token保存至缓存中,并设置有效时间 redisUtils.set("userToken-" + user.getId(), token, 2L * 60); } } //校验token是否为空 if(StringUtils.isEmpty(token)){ return ResultDTO.failure(new ResultError(UserError.PASSWORD_OR_NAME_IS_ERROR)); } tokenMap.put("token", token); tokenMap.put("user",user); } catch (Exception e) { e.printStackTrace(); } //将登录用户信息和token返回给前端 return ResultDTO.success(tokenMap); }public ResultDTO login(String name, String password) { Map
tokenMap = new HashMap<>(); String token = null; try { User user = userMapper.findByUsername(name); //查询用户登录的账号是否存在 if(user == null){ return ResultDTO.failure(new ResultError(UserError.EMP_IS_NULL_EXIT)); }else{ //校验用户密码是否正确 if(!user.getPassword().equals(password)){ return ResultDTO.failure(new ResultError(UserError.PASSWORD_OR_NAME_IS_ERROR)); }else { // 获取token,将 user id 、userName保存到 token 里面 token = JwtUtil.sign(user.getUsername(),user.getId(),user.getPassword()); //将用户token保存至缓存中,并设置有效时间 redisUtils.set("userToken-" + user.getId(), token, 2L * 60); } } //校验token是否为空 if(StringUtils.isEmpty(token)){ return ResultDTO.failure(new ResultError(UserError.PASSWORD_OR_NAME_IS_ERROR)); } tokenMap.put("token", token); tokenMap.put("user",user); } catch (Exception e) { e.printStackTrace(); } //将登录用户信息和token返回给前端 return ResultDTO.success(tokenMap); }

拦截器代码实现

public class AuthenticationInterceptor implements HandlerInterceptor {    @Autowired    private RedisUtils redisUtils;    @Autowired    UserMapper userMapper;    @Override    public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object object) throws Exception {        // 从 http 请求头中取出 token        String token = httpServletRequest.getHeader("token");        // 如果不是映射到方法直接通过        if(!(object instanceof HandlerMethod)){            return true;        }        HandlerMethod handlerMethod=(HandlerMethod)object;        Method method=handlerMethod.getMethod();        PrintWriter out = null ;        //检查有没有需要用户权限的注解        if (method.isAnnotationPresent(TokenRequired.class)) {            TokenRequired userLoginToken = method.getAnnotation(TokenRequired.class);            if (userLoginToken.required()) {                // 执行认证                //1.如果token为空,返回token为空                if (token == null) {                    out = httpServletResponse.getWriter();                    out.append(JSON.toJSONString(ResultDTO.failure(new ResultError(UserError.NONE_TOKEN))));                    return false;                }                // 2.获取 token 中的 user id,校验token                String userId;                try {                    userId = JWT.decode(token).getClaim("userId").asString();                } catch (JWTDecodeException j) {                    out = httpServletResponse.getWriter();                    out.append(JSON.toJSONString(ResultDTO.failure(new ResultError(UserError.TOKEN_CHECK_ERROR))));                    return false;                }                //3.查询token中登录用户数据库中是否存在                User user = userMapper.findUserById(userId);                if (user == null) {                    out = httpServletResponse.getWriter();                    out.append(JSON.toJSONString(ResultDTO.failure(new ResultError(UserError.EMP_IS_NULL_EXIT))));                    return false;                }                // 验证 token                try {                    if (!JwtUtil.verity(token, user.getPassword())) {                        //如果token检验失败,查询redis中是否存在,redis为空,用户重新登录                        if (redisUtils.get("userToken-" + user.getId()) == null) {                            out = httpServletResponse.getWriter();                            out.append(JSON.toJSONString(ResultDTO.failure(new ResultError(UserError.TOKEN_IS_VERITYED))));                            return false;                        }                        //如果redis不为空,刷新token                        String reToken = JwtUtil.sign(user.getUsername(),user.getId(),user.getPassword());                        //将刷新后的token保存在redis中                        redisUtils.set("userToken-" + user.getId(), reToken, 2L * 60);                        out = httpServletResponse.getWriter();                        out.append(JSON.toJSONString(ResultDTO.failure(reToken,new ResultError(UserError.TOKEN_IS_EXPIRED))));                        return false;                    }                } catch (Exception e) {                    out = httpServletResponse.getWriter();                    out.append(JSON.toJSONString(ResultDTO.failure(new ResultError(UserError.TOKEN_CHECK_ERROR))));                    return false;                }            }        }        return true;    }    @Override    public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {    }    @Override    public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {    }}

 

拦截器请求业务处理流程

  1. 判断用户请求资源是否携带token,未携带则权限不足;
  2. 携带token则从token中获取用户id,并校验数据库,查看该用户是否存在;不存在则token无效不予放行;
  3. 校验token是否过期,如果是正常token且未过期,放行。
  4. 正常token但已过期:查询redis中该token是否存在,如果缓存中存在则刷新token,并将刷新后的token重新放入redis,使用刷新后的token请求资源;缓存中不存在,则token刷新期限已过,用户需重新登录。

JWT刷新机制的基本业务判断流程

  1. 用户登录,生成token,并将token写入缓存;
  2. 用户访问页面,前端请求相关接口,经过拦截器,拦截器中从 http 请求头中取出 token;
  3. 检验token是否为空,如果token为空,访问失败;token不为空,则查询用户信息并校验token;
  4. token不为空,且用户存在,则token正确,进行token过期校验;
  5. token已过期,查询缓存中token是否存在,存在进行刷新生成新的token,返回给前端,并将新的token放入缓存;不存在则用户需重新登录。

Done

token超时刷新策略

转载地址:http://xrazb.baihongyu.com/

你可能感兴趣的文章
Intellij IDEA使用(十四)—— 在IDEA中创建包(package)的问题
查看>>
Redis学习笔记(四)—— redis的常用命令和五大数据类型的简单使用
查看>>
Win10+VS2015编译libcurl
查看>>
Windows下使用jsoncpp
查看>>
Ubuntu下测试使用Nginx+uWsgi+Django
查看>>
Windows下编译x264
查看>>
visual studio调试内存泄漏工具
查看>>
开源Faac实现PCM编码AAC
查看>>
Windows下wave API 音频采集
查看>>
借船过河:一个据说能看穿你的人性和欲望的心理测试
查看>>
AndroidStudio 导入三方库使用
查看>>
Ubuntu解决gcc编译报错/usr/bin/ld: cannot find -lstdc++
查看>>
解决Ubuntu14.04 - 16.10版本 cheese摄像头灯亮却黑屏问题
查看>>
解决Ubuntu 64bit下使用交叉编译链提示error while loading shared libraries: libz.so.1
查看>>
Android Studio color和font设置
查看>>
Python 格式化打印json数据(展开状态)
查看>>
Centos7 安装curl(openssl)和libxml2
查看>>
Centos7 离线安装RabbitMQ,并配置集群
查看>>
Centos7 or Other Linux RPM包查询下载
查看>>
运行springboot项目出现:Type javax.xml.bind.JAXBContext not present
查看>>