本笔记内容为尚硅谷谷粒商城购物车ThreadLocal用户身份鉴别部分
目录
ThreadLocal
1.导入依赖
2.编写配置
3.配置Session
5.编写To与常量
6.编写拦截器
7.添加拦截器的WebConfig配置类
8.Debug测试UserInfoTo中是否有数据
ThreadLocal
ThreadLocal是一个池,也就是一个Map,存放的是一个线程下的共享数据,即同一个线程共享ThreadLocal中的数据
- 核心原理是:Map<Thread,Object> threadLocal
- 在每个线程中都创建了一个 ThreadLocalMap 对象,每个线程可以访问自己内部 ThreadLocalMap 对象内的 value。线程之间互不干扰
- 一次请求进来: 拦截器 ==>> Controller ==>> Service ==>> dao 用的都是同一个线程
因为拦截器在拦截请求之后会向controller层传递一个
UserInfoVo
对象,所以为了获取到这个对象,使用ThreadLocal.
所以在拦截器处设置一个静态的ThreadLocal变量:
public static ThreadLocal<UserInfoTo> threadLocal = new ThreadLocal<UserInfoTo>();
放上数据后就可以传递给后面。
1.导入依赖
导入redis和SpringSession的依赖
- 将购物车数据存储至Redis中,因此,需要导入Spring整合Redis的依赖。项目上线之后,应该有一个专门的Redis负责存储购物车的数据不应该使用缓存的Redis。
- 判断用户是否登录则通过判断Session中是否有用户的数据,因此,导入SpringSession的依赖
<!-- Spring整合Redis依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- SpringSession的依赖 -->
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-data-redis</artifactId>
</dependency>
2.编写配置
# 配置redis
spring.redis.host=192.168.10.10
spring.redis.port=6379
3.配置Session
@EnableRedisHttpSession
@Configuration
public class GulimallSessionConfig {
/**
* 子域问题共享解决
*/
@Bean
public CookieSerializer cookieSerializer() {
DefaultCookieSerializer cookieSerializer = new DefaultCookieSerializer();
cookieSerializer.setDomainName("gulimall.com");
cookieSerializer.setCookieName("GULIMALLSESSION");
return cookieSerializer;
}
/**
* 使用json序列化方式来序列化对象数据到redis中
*/
@Bean
public RedisSerializer<Object> springSessionDefaultRedisSerializer() {
return new GenericJackson2JsonRedisSerializer();
}
}
4.cookie中的user-key说明
用户第一次访问购物车的时候为用户颁发一个Cookie(无论是否登录都颁发),cookie的key就叫user-key
,value在后台随机生成。同时要设置cookie的过期时间为1个月,还有cookie的作用域。如果手动清除user-key
,那么临时购物车的购物项也被清除,所以user-key
是用来标识和存储临时购物车数据的。
用户登录之后点击购物车:
访问Session中存储的之前登录的用户信息,并进行用户身份信息(userId)的封装,userId只有登录过的UserInfoTo才有,没登陆过的这个属性值为null,这个属性就是购物车取数据时是取用户购物车还是临时购物车的一个判断。
用户未登录的时候点击购物车:
总之没登陆的用户userId为空,登录的userId有值,但是登不登陆其都有userKey这个属性的值。
Cookie中有 user-key,则表示有临时用户,访问临时用户的购物车信息,把user-key进行用户身份信息的封装
Cookie中没有 user-key,则表示没有临时用户
创建一个临时用户即生成一个name为user-key的cookie临时标识,过期时间为一个月,以后每次浏览器进行访问购物车的时候都会携带user-key。user-key 是用来标识和存储临时购物车数据的,处理完后会返回这个user-key
以上封装好的信息会放进ThreadLocal交给controller进行处理。
5.编写To与常量
6.编写拦截器
想要使用
SpringMVC
的拦截器机制,需要先编写一个类,实现HandlerInterceptor
接口,根据业务需求重写接口中的方法。拦截器作用为在调用购物车的接口(实际上拦截器拦截所有请求,不只是购物车的请求)前,先通过 Session 信息判断是否登录,并分别进行用户身份信息的封装,并把
user-key
放在 Cookie 中。重写了两个方法:
- 在执行目标方法之前,判断用户的登录状态,并封装传递给controller目标请求
- 在执行目标方法之后,若没有临时用户则分配临时用户,让浏览器保存
同时,还要指定拦截器作用在那些请求上。
注意细节:整合SpringSession之后,Session获取数据都是从Redis中获取的
/**
* 在执行目标方法前,先判断用户登录状态。并封装用户信息传递给Controller
*/
public class CartInterceptor implements HandlerInterceptor {
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();
MemberRespVo member = (MemberRespVo) session.getAttribute(AuthServerConstant.LOGIN_USER);
if (member != null) {
//用户登录
userInfoTo.setUserId(member.getId());
}
Cookie[] cookies = request.getCookies();
if (cookies != null && cookies.length > 0) {
for (Cookie cookie : cookies) {
String name = cookie.getName();
if (name.equals(CartConstant.TEMP_USER_COOKIE_NAME)) {
userInfoTo.setUserKey(cookie.getValue());
userInfoTo.setTempUser(true);
}
}
}
//目标方法执行之前
//如果没有临时用户,分配临时用户
if (StringUtils.isEmpty(userInfoTo.getUserKey())) {
String uuid = UUID.randomUUID().toString();
userInfoTo.setUserKey(uuid);
}
threadLocal.set(userInfoTo);
return true;
}
/*
业务执行之后
*/
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception {
UserInfoTo userInfoTo = threadLocal.get();
//如果没有任何信息,创建临时用户,保存cookie
if (!userInfoTo.isTempUser()) {
Cookie cookie = new Cookie(CartConstant.TEMP_USER_COOKIE_NAME, userInfoTo.getUserKey());
cookie.setDomain("gulimall.com");
//单位秒
cookie.setMaxAge(CartConstant.TEMP_USER_COOKIE_TIMEOUT);
response.addCookie(cookie);
}
}
}
7.添加拦截器的WebConfig配置类
配置拦截器,否则拦截器不生效
8.Debug测试UserInfoTo中是否有数据
结束!