🥩 登录验证优化
由上面的登录验证可知,我们对一些需要用户登录验证的功能设置了拦截器,如果验证通过会刷新token的有效期,这样的话只要用户一直访问我们拦截的功能就可以一直保持token是有效的。但是,如果用户登陆之后的操作一直是不需要验证的,那也就意味着token的有效期一直不会刷新,这样的话30分钟之后token就会失效用户验证就会失败,这样显然是不合理的
于是我们可以使用两个拦截器完成,最前面的负责拦截所有的请求,获取token、从redis中查询用户,将查询结果放到ThreadLocal(可能存null)、刷新token有效期,最后直接放行;后面的拦截器只负责判断有没有从redis中查询到用户,他从ThreadLocal获取查询结果,判断有则放行无则拦截
创建两个拦截器
/** * @author : mereign * @date : 2022/5/5 - 10:31 * @desc : 前置拦截器,拦截所有请求,前置工作 */ @Component public class RefreshTokenInterceptor implements HandlerInterceptor { @Autowired private StringRedisTemplate stringRedisTemplate; @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { // 获取请求头中的token信息 String token = request.getHeader("authorization"); if (StrUtil.isBlank(token)) { // token为空 直接放行 return true; } // 根据token获取redis中的用户value Map<Object, Object> userMap = stringRedisTemplate.opsForHash().entries(RedisConstants.LOGIN_USER_KEY + token); HttpSession session = request.getSession(); // 判断用户是否存在 if (userMap.isEmpty()) { // 用户不存在 直接放行 return true; } // 用户存在,将hash数据转换为userDTO,存信息到ThreadLocal UserDTO userDTO = BeanUtil.fillBeanWithMap(userMap, new UserDTO(), false); UserHolder.saveUser(userDTO); // 刷新token有效期,放行 stringRedisTemplate.expire(RedisConstants.LOGIN_USER_KEY + token, RedisConstants.LOGIN_USER_TTL, TimeUnit.MINUTES); return true; } @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { UserHolder.removeUser(); } }
/** * @author : mereign * @date : 2022/5/5 - 10:31 * @desc : 登录拦截器,拦截需要拦截的请求,判断登录信息 */ @Component public class LoginInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { // 判断登录 if (UserHolder.getUser() == null) { response.setStatus(401); return false; } return true; } @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { } }
创建完拦截器之后要将两个拦截器通过配置类配置到容器中生效,多个拦截器的优先级,默认按照添加顺序执行优先级,但是也可以使用order方法指定优先级,按参数的大小排序优先级,参数越小优先级越高
/** * @author : mereign * @date : 2022/5/5 - 10:43 * @desc : 配置类注册拦截器 */ @Configuration public class MvcConfig implements WebMvcConfigurer { @Autowired private RefreshTokenInterceptor refreshTokenInterceptor; @Autowired private LoginInterceptor loginInterceptor; @Override public void addInterceptors(InterceptorRegistry registry) { // 前置拦截器 registry.addInterceptor(refreshTokenInterceptor) .addPathPatterns("/**") .order(0); // 后置拦截器 registry.addInterceptor(loginInterceptor) .excludePathPatterns( "/shop/**", "/voucher/**", "/shop-type/**", "/upload/**", "/blog/hot", "/user/code", "/user/login" ) .order(1); } }