1.首先进行登录操作,代码已经假设你从数据库拿到了用户信息保存在变量userinfo中,将token返回给浏览器保存,并将token和userinfo保存在redis中。

@Resource
    private RedisTemplate redisTemplate;

    @RequestMapping("/login")
    public R token(HttpServletRequest request, HttpServletResponse response) {

      
            //假设此时已经从数据库获取到用户信息在userinfo
           
            String token = UUID.randomUUID().toString();
            Cookie cookie=new Cookie("tokenUser",token);
            cookie.setMaxAge(60 * 60 * 24 * 7);//有效期设为7天
            cookie.setPath("/");//设置路径
            response.addCookie(cookie);//响应回游览器
            RedisCache redis=new RedisCache(redisTemplate);
            redis.set(token,userinfo,60 * 60 * 24 * 7);//有效期设为7天

            return R.ok(userinfo,"登录成功");
       

    }

2.新建一个类型,这个是全局线程。当用户信息经过token验证从redis拿出来之后要保存在这个里面,想用的时候从这个里面拿取就行了。

public class UserThreadLocal {


    private static ThreadLocal<User> userThread =new ThreadLocal<User>();


    public static void set(User user){
        userThread.set(user);
    }

    public static User get(){
        return userThread.get();
    }

    //防止内存泄漏
    public static void remove(){
        userThread.remove();
    }
}

3.新建一个拦截器,业务逻辑是 获取浏览器token,再从redis获取userinfo,将userinfo保存在UserThreadLocal线程中。

@Configuration
public class LoginInterceptor implements HandlerInterceptor {


    @Resource
    private RedisTemplate redisTemplate;

    @Autowired
    private static ObjectMapper objectMapper = new ObjectMapper();

    //在执行COntroller方法之前执行
    /**
     * boolean 表示是否放行
     * 	 true:放行 用户可以跳转页面
     * 	 false:拦截  之后给定重定向路径
     *
     * 业务逻辑:
     * 	1.判断用户客户端是否有Cookie/token数据
     * 		如果用户没有token则重定向到用户登陆页面
     *  2.如果用户token中有数据,则从redis缓存中获取数据
     *  	如果redis中数据为null,则重定向到用户登陆页面
     *  3.如果reids中有数据,则放行请求.
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
            throws Exception {
        String token = "";
        //获取Cookie数据
        Cookie[] cookies = request.getCookies();
        for (Cookie cookie : cookies) {
            if("tokenUser".equals(cookie.getName())){
                token = cookie.getValue();
                break;
            }
        }
        //判断Cookie是否为null
        if(!StringUtils.isEmpty(token)){
            System.out.println(token);
            //检测缓存中是否有该数据
            RedisCache redis=new RedisCache(redisTemplate);
            Object userinfo= redis.get(token);

            System.out.println(userinfo);

            if(!StringUtils.isEmpty(userinfo)){

                //将userJSON转化为User对象
                User user= (User) userinfo;

                UserThreadLocal.set(user);
                //用户已经登陆 放行请求
                return true;
            }
        }
        //表示用户没有登陆
        response.sendRedirect("/login.html");
        return false;
    }

    //执行完业务逻辑后拦截
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
                           ModelAndView modelAndView) throws Exception {
        // TODO Auto-generated method stub

    }

    //返回页面之前拦截
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
            throws Exception {

        //将ThreadLocal数据清空
        UserThreadLocal.remove();
    }



}

3.新建一个拦截器配置类,设置要访问拦截器的路径,开放登录接口和静态资源接口,其他都配置成要访问拦截器

@Configuration
public class InterceptorConfig implements WebMvcConfigurer {

    @Autowired
    private LoginInterceptor loginInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry){
     //添加对用户未登录的拦截器,并添加排除项
        registry.addInterceptor(loginInterceptor).addPathPatterns("/**")//拦截所有
                .excludePathPatterns("/js/**","/dist/images/**")//排除样式、脚本、图片等资源文件
                .excludePathPatterns("/login")//排除登录
                .excludePathPatterns("/","/index");

    }
}

4.获取用户信息。

@RequestMapping("/getuserinfo")
    public R getuserinfo() {
        User user = UserThreadLocal.get();
        System.out.println(user);
        return R.ok(user,"获取用户信息成功");
    }

 问:ThreadLocal如何确保每次都能取到对应的信息?

 答:每次执行请求时拦截器将用户信息保存在ThreadLocal中,ThreadLocal每次调用set时是一个独立的线程,当另一个用户调用ThreadLocal的set时方法 时,就会新建另一个线程,线程之间互不影响,当对应线程在调用get时候,就会请求到set时候的信息。

 问:怎么保证线程过多内存不会溢出呢?

 答:在拦截器的afterCompletion方法中添加ThreadLocal线程删除方法,这样每次请求结束后会将ThreadLocal线程中的数据删除,这样保证了线程不会太多,内存不会溢出。