类似京东一样,如果我们不登陆,会给我们分配一个临时用户的身份。不论我们是否登录都可以使用购物车。未登录状态下的购物车的内容会在登录后添加到登录状态的购物车中。
1、controller层:我们通过访问cart.gulimall.com/cart.list访问购物车
2、在访问这个请求之前,我们需要先判断该是否有用户登录,所以我们编写了下面这个拦截器
package com.gulimall.cart.interceptor;
import com.gulimall.cart.to.UserInfoTo;
import com.gulimall.common.constant.AuthServerConstant;
import com.gulimall.common.constant.CartConstant;
import com.gulimall.common.vo.MemberRespVo;
import org.apache.commons.lang.StringUtils;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.util.UUID;
/**
* 在执行目标方法执行前,判断用户的登录状态,并封装传递给controller的目标请求
*/
public class CartInterceptor implements HandlerInterceptor {
//创建ThreadLocal来共享数据
public static ThreadLocal<UserInfoTo> threadLocal = new ThreadLocal<>();
/**
* 在执行目标方法之前, 如果用户登录了,给UserInfoTo设置它的id
* 如果没有登录,就创建一个cookie(user-key),并给UserInfoTo设置
* @param request
* @param response
* @param handler
* @return
* @throws Exception
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
UserInfoTo userInfoTo = new UserInfoTo();
HttpSession session = request.getSession();
MemberRespVo memberRespVo = (MemberRespVo) session.getAttribute(AuthServerConstant.LOGIN_USER);
if (memberRespVo != null){
//用户已经登录
userInfoTo.setUserId(memberRespVo.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);//是临时用户
}
}
}
//如果没有临时用户一定分配一个临时用户(还要让浏览器以cookie方式保存)
if (StringUtils.isEmpty(userInfoTo.getUserKey())){
String uuid = UUID.randomUUID().toString();
userInfoTo.setUserKey(uuid);
}
//目标方法执行之前,放入共享数据
threadLocal.set(userInfoTo);
return true; //不管是否登录,都放行
}
/**
* 在业务处理之后,让浏览器保存cookie
* @param request
* @param response
* @param handler
* @param modelAndView
* @throws Exception
*/
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
UserInfoTo userInfoTo = threadLocal.get();
if (!userInfoTo.getTempUser()){ //如果是临时用户就不用放cookie
Cookie cookie = new Cookie(CartConstant.TEMP_USER_COOKIE_NAME, userInfoTo.getUserKey());
//设置cookie的作用域
cookie.setDomain("gulimall.com");
//设置cookie的失效时间
cookie.setMaxAge(CartConstant.TEMP_USER_COOKIE_TIMEOUT);
response.addCookie(cookie);
}
}
}
其中ThreadLocal是用来存储共享数据的,只要是同一个线程,都可以取到ThreadLocal中的值。
在preHandle中,我们通过session中是否存储了登录用户的信息来判断用户是否已经登录。如果用户已经登录过了,就从session中取到这个用户的id,然后赋值给UserInfoTo。然后查看cookie中是否有user-key这个常量。如果已经设置过了user-key,设置这个UserInfoTo为临时用户。如果userInfoTo的user-key没有设置过,说明它不是临时用户,所以我们给他的user-key赋值,将他设置为临时用户。然后将UserInfoTo放入共享数据中。
在postHandler中,我们主要工作是给浏览器添加cookie,如果已经是临时用户了(即在preHanlde中已经设置过了),就不用再设置浏览器的cookie了
综上所述:
如果用户访问网站没有登录,当访问购物车时,在preHandle中,UserInfoTo的id一定为空。并且因为是第一次访问购物车,所以request.getCookies()遍历浏览器的cookie也没有临时用户的user-key,这时我们会给它分配一个临时用户的身份,同时将UserInfoTo放入共享数据中。
在postHandle中,由于第一次访问购物车,userInfoTo.getTempUser()为false,即不是临时用户。但我们在preHandle中已经给它分配临时用户的身份了,即user-key,这是我们只需要给浏览器添加cookie就行了。
当用户第二次访问购物车时,就会在request.getCookies()遍历时获取到user-key,之后在postHandle中就不会再给浏览器添加cookie了(除非过期);