ThreadLocal authentication, shopping cart

What is the role of ThreadLocal?
ThreadLocal is a good idea to solve the thread safety problem. It solves the conflict of concurrent access to variables by providing an independent variable copy for each thread. In many cases, ThreadLocal is simpler and more convenient than directly using the synchronized synchronization mechanism to solve thread safety issues, and the resulting program has higher concurrency.

What is the application scenario of ThreadLocal?
In Java's multithreaded programming, in order to ensure safe access to shared variables by multiple threads, synchronized is usually used to ensure that only one thread operates on shared variables at a time. In this case, you can put the class variable in the ThreadLocal type object, so that the variable has an independent copy in each thread, and there will be no phenomenon that one thread reads the variable and is modified by another thread. The most common ThreadLocal usage scenario is to solve database connection, Session management, etc. A few scenarios will be listed below

Insert picture description here
In this way, we can get user data through ThreadLocal in any place.

Demand analysis:
* This is a shopping cart scenario. You can add a shopping cart when the user is not logged in, and you can also add a shopping cart after logging in.
* The browser has a cookie: user-key, which identifies the user's identity, and expires in one month.
* If you use jd's shopping cart function for the first time, you will be given a temporary user identity.
* The browser will save it later and bring it with you every time you visit This cookie
*
* Login session Yes
* No login, do according to the user-key in the cookie
* First time: If there is no temporary user, help create a temporary user --> Use interceptor
* @return to
introduce dependency:

<!--        引入redis-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>

        <!--        整合Spring Session完成session共享问题 微服务自治,就不放在common里了-->
        <dependency>
            <groupId>org.springframework.session</groupId>
            <artifactId>spring-session-data-redis</artifactId>
        </dependency>

Modify the configuration information of plication.yml

  #配置Redis缓存
  spring:
	  redis:
	    host: 81.68.112.20
	    port: 6379
  #整合Spring Session 指定session是存到redis里
	  session:
	    store-type: redis

First create an interceptor

**
 * @author 孟享广
 * @date 2021-02-03 3:01 下午
 * @description 在执行目标方法之前,判断用户的登录状态,并封装传递给controller的目标请求
 */
public class CartInterceptor implements HandlerInterceptor {
    
    

    //ThreadLocal 同一线程上信息共享
    public static ThreadLocal<UserInfoTo> threadLocal = new ThreadLocal<>();

    /**
     * 在目标方法执行之前
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    
    

        UserInfoTo userInfoTo = new UserInfoTo();
        HttpSession session = request.getSession();
        MemberResVo memberResVo = (MemberResVo) session.getAttribute(AuthServiceConstant.LOGIN_USER);
        if (memberResVo != null) {
    
    
            //说明用户登录了
            userInfoTo.setUserId(memberResVo.getId());
        }

        //只要有user-key 就赶紧取出value
        Cookie[] cookies = request.getCookies();
        if (cookies != null && cookies.length > 0) {
    
    
            //有cookie 可能是临时用户,但是此方法针对登录用户
            for (Cookie cookie : cookies) {
    
    
                String name = cookie.getName();
                if (name.equals(CartServiceConstant.TEMP_USER_COOKIE_NAME)) {
    
    
                    userInfoTo.setUserKey(cookie.getValue());
                    //执行到这,说明是临时用户
                    userInfoTo.setTempUser(true);
                }
            }
        }

        //如果没有登录 就准备临时set一个cookie,首先设置To的userKey
        if (StringUtils.isEmpty(userInfoTo.getUserKey())) {
    
    
            String uuid = UUID.randomUUID().toString();
            userInfoTo.setUserKey(uuid);
        }

        //目标方法执行前,放入 threadLocal
        threadLocal.set(userInfoTo);

        //只要来到目标方法都放行 无条件放行
        return true;
    }

    /**
     * 业务执行之后,让浏览器保存cookie
     * 分配临时用户,让浏览器保存
     */
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
    
    

        UserInfoTo userInfoTo = threadLocal.get();
        //如果没有临时用户,一定要保存一个临时用户
        if (!userInfoTo.isTempUser()) {
    
    
            //不是临时用户
            Cookie cookie = new Cookie(CartServiceConstant.TEMP_USER_COOKIE_NAME, userInfoTo.getUserKey());
            //设置cookie作用域
            cookie.setDomain("gulimall.com");
            //cookie的过期时间
            cookie.setMaxAge(CartServiceConstant.TEMP_USER_COOKIE_TIMEOUT);
            response.addCookie(cookie);
        }
    }
}

Get user data through ThreadLocal in Controller.

    /**
     * 浏览器有一个cookie:user-key:标识用户身份  一个月过期
     * 假如是第一次登录,都会给一个临时身份
     * 浏览器保存以后,每次访问都会带上这个cookie
     */
    @GetMapping("/cart.html")
    public String cartListPage(Map<String, Cart> map) throws ExecutionException, InterruptedException {
    
    

        //1 快速得到用户信息,ThreadLocal获取用户信息
        UserInfoTo userInfoTo = CartInterceptor.threadLocal.get();
        System.out.println(userInfoTo);
        return "cartList";
    }

Guess you like

Origin blog.csdn.net/u014496893/article/details/114014944