Spring项目使用过滤器和ThreadLocal实现用户态校验与保存

mario 2020年09月28日 183次浏览

前言

使用拦截器校验用户登录态,满足条件则继续执行控制器逻辑,不满足直接跳到登录页。大多数时候,控制器逻辑需要用到登录信息,这时候可以再从Redis取出登录信息,或者在校验完成后用ThreadLocal保存起来。

UserHolder

定义UserHolder类:

package cn.liaoxiaojie.seckill.util;

import cn.liaoxiaojie.seckill.entity.User;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;

@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class UserHolder {
    private static final ThreadLocal<User> LOCAL = new ThreadLocal<>();
    public static void set(User user) {
        LOCAL.set(user);
    }
    public static User get() {
        return LOCAL.get();
    }
    public static void remove() {
        LOCAL.remove();
    }
}

拦截器

定义拦截器实现登录校验:

public class AuthorityInterceptor implements HandlerInterceptor {

    @Autowired
    private RedisService redisService = null;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
        //利用反射查看被调用方法是否要求登录
        if (!(handler instanceof HandlerMethod)) { //可能拦截到的是静态资源请求
            return true;
        }
        //请求controller中的方法名
        HandlerMethod handlerMethod = (HandlerMethod) handler;
        Access access = handlerMethod.getMethodAnnotation(Access.class);
        if (access == null) return true;
        boolean requireLogin = access.requireLogin();
        if (!requireLogin) return true;
        boolean login = checkLogin(request);
        if (login) return true;
        else {
            try {
                response.sendRedirect("/login");
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        return false;
    }

    //校验登录态
    private boolean checkLogin(HttpServletRequest request) {
        String token = CookieUtil.readLoginToken(request);
        if (token == null) return false;
        User user = redisService.get(UserKeyPrefix.TOKEN, token, User.class);
        if (user == null) return false;
        User tmp = UserHolder.get();
        if (tmp == null) UserHolder.set(user);
        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.remove();
    }
}

登录校验成功后将用户信息保存到UserHolder。

注册拦截器

@Configuration
public class WebConfig implements WebMvcConfigurer {

    @Autowired
    private AuthorityInterceptor authorityInterceptor = null;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        InterceptorRegistration registration = registry.addInterceptor(authorityInterceptor);
        registration.addPathPatterns("/**");
        registration.excludePathPatterns(                           //添加不拦截路径
                "/login",                                           //登录
                "/register",                                        //注册
                "/**/*.html",                                       //html静态资源
                "/**/*.js",                                         //js静态资源
                "/**/*.css",                                        //css静态资源
                "/**/*.jpg",
                "/**/*.woff",
                "/**/*.ttf"
        );
    }
}

使用

@RequestMapping(value = "", method = RequestMethod.POST)
@ResponseBody
@Access
public Result<?> seckill(Long id) {

    User user = UserHolder.get();
    System.out.println(user.toString());

    return Result.success(1);
}

测试结果:
image.png

参考