本文共 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,而此时进行接口请求时通常有以下几种状态:
public ResultDTO login(String name, String password) { MaptokenMap = 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 { }}
Done
转载地址:http://xrazb.baihongyu.com/