@Component
public class AlphaInterceptor implements HandlerInterceptor {
private static final Logger logger = LoggerFactory.getLogger(AlphaInterceptor.class);
//在Controller之前执行
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
logger.debug("preHandle: " + handler.toString());
//可以在request和response中加逻辑
return true;//返回false,不往下执行
}
//在Controller之后执行,相当于对Controller最后的补充,所以会给modelAndView
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
logger.debug("postHandle: " + handler.toString());
}
//在模板引擎TemplateEngine之后,最后执行
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
logger.debug("afterCompletion: " + handler.toString());
}
}
注入拦截器,静态资源随意访问,静态资源的路径被排除,要拦截的路径是/register
, /login
。
/**/*.css
表示static目录下任意文件中的css文件.
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
//注入拦截器
@Autowired
private AlphaInterceptor alphaInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(alphaInterceptor)
.excludePathPatterns("/**/*.css", "/**/*.js", "/**/*.png", "/**/*.jpg", "/**/*.jpeg")
.addPathPatterns("/register", "/login");
}
}
@Component
public class LoginTicketInterceptor implements HandlerInterceptor {
@Autowired
private UserService userService;
@Autowired
private HostHolder hostHolder;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 从request获取name为ticket的cookie value
String ticket = CookieUtil.getValue(request, "ticket");
if (ticket != null) {
// 查询凭证实例oginTicket
LoginTicket loginTicket = userService.findLoginTicket(ticket);
// 检查凭证是否存在、有效,过期时间晚于当前时间
if (loginTicket != null && loginTicket.getStatus() == 0 && loginTicket.getExpired().after(new Date())) {
// 根据凭证查询用户实例user loginTicket->userId->user
User user = userService.findUserById(loginTicket.getUserId());
hostHolder.setUsers(user);// 保存用户数据,考虑到多线程并发,线程隔离用ThreadLocal
}
}
return true;//如果是false后面就不执行了
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
User user = hostHolder.getUsers();
if (user != null && modelAndView != null) {
modelAndView.addObject("loginUser", user);
}
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
hostHolder.clear();
}
}
CookieUtil
自定义CookieUtil,获取对应cookie的value
public class CookieUtil {
//静态方法,不用容器调用了
public static String getValue(HttpServletRequest request, String name) {
if (request == null || name == null) {
throw new IllegalArgumentException("参数为空!");
}
Cookie[] cookies = request.getCookies();//得到了cookies列表
if (cookies != null) {
for (Cookie cookie : cookies) {
if (cookie.getName().equals(name)) {//遍历,找对应name的value
return cookie.getValue();
}
}
}
return null;
}
}
HostHolder
自定义HosthHoder.java,声明ThreadLocal变量保存user数据,保证线程隔离
@Component
public class HostHolder {
private ThreadLocal<User> users = new ThreadLocal<>();
public void setUsers(User user) {
users.set(user);
}
public User getUsers() {
return users.get();
}
public void clear() {
users.remove();
}
}
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
@Autowired
private LoginTicketInterceptor loginTicketInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(loginTicketInterceptor)
.excludePathPatterns("/**/*.css", "/**/*.js", "/**/*.png", "/**/*.jpg", "/**/*.jpeg");
}
}
ThreadLocal如何保证线程隔离?
每个线程有一个ThreadLocalMap,key是ThreadLocal引用,value是要保存的数据。访问threadlocal变量的线程,都会在自己工作内存本地,保存该变量的副本,也就意味着将一个共享唯一的资源复制成多份,由各个线程独占。