1.应用场景
- 这个拦截器其实和面向切面编程的思想很像,主要用来处理一些公共的可复用的一些请求,这些东西每次都实现很麻烦,所有我们直接将其用拦截器编写会方便很多。
2.应用
- springboot中提供了HandlerInterceptor接口,里面提供了三个方法
public interface HandlerInterceptor {
default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
return true;
}
default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception {
}
default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception {
}
}
3.案例
- 我们编写一个类去实现这个接口看一看
- 然后我们去编写一个配置类,让这个拦截器具体要拦截哪些请求,然后启动项目
- 最后可以得出以下代码中的结论
- 拦截器类:
package com.nowcoder.community.controller.interceptor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.swing.*;
@Component
public class AlphaInterceptor implements HandlerInterceptor {
private static final Logger logger = LoggerFactory.getLogger(AlphaInterceptor.class);
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
logger.debug("preHandle :" + handler.toString());
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
logger.debug("postHandle :" + handler.toString());
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
logger.debug("afterCompletion :" + handler.toString());
}
}
import com.nowcoder.community.controller.interceptor.AlphaInterceptor;
import com.nowcoder.community.controller.interceptor.LoginTicketInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@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");
}
}
- 然后我们启动项目看一下
- 可以清楚看到请求的过程,所以我们可以利用这三个方法的特性完成一些特定的功能,比如用户的识别。下面我们就来看看这个案例
4.登录案例(后台代码)
- 我们在登录后,无论哪个页面都应该保存我的个人信息,那么就意味着每次都要发请求,每次都要写肯定是非常麻烦的,我们可以借助拦截器,让他每次帮我们做就好了,我们只需要写一次就够了。
- 首先和上面一样,创建一个拦截器类和一个配置类
- 拦截器类:
package com.nowcoder.community.controller.interceptor;
import com.nowcoder.community.entity.LoginTicket;
import com.nowcoder.community.entity.User;
import com.nowcoder.community.service.UserService;
import com.nowcoder.community.util.CookieUtil;
import com.nowcoder.community.util.HostHolder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Date;
@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 {
String ticket = CookieUtil.getValue(request, "ticket");
if(ticket != null){
LoginTicket loginTicket = userService.findLoginTicket(ticket);
if(loginTicket != null && loginTicket.getStatus() == 0 && loginTicket.getExpired().after(new Date())){
User user = userService.findUserById(loginTicket.getUserId());
hostHolder.setUser(user);
}
}
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
User user = hostHolder.getUser();
if(user != null && modelAndView != null){
System.out.println(user.getUsername());
modelAndView.addObject("loginUser", user);
}
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
hostHolder.clear();
}
}
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
@Autowired
private AlphaInterceptor alphaInterceptor;
@Autowired
private LoginTicketInterceptor loginTicketInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(alphaInterceptor)
.excludePathPatterns("/**/*.css", "/**/*.js", "/**/*.png", "/**/*.jpg", "/**/*.jpeg")
.addPathPatterns("/register", "/login");
registry.addInterceptor(loginTicketInterceptor)
.excludePathPatterns("/**/*.css", "/**/*.js", "/**/*.png", "/**/*.jpg", "/**/*.jpeg");
}
}
- 上面的拦截器类中,我们用到了多线程的知识,用到了ThreadLocal类来帮我们处理并发的情况,因为如果只是单纯的放入容器很有可能会出现问题。用多线程就不会有问题了。接下来看看怎么编写,其实也很简单,我们就用这个类(因为以后要复用,我们单独封装一下)代替session对象
@Component
public class HostHolder {
private ThreadLocal<User> users = new ThreadLocal<>();
public void setUser(User user){
users.set(user);
}
public User getUser(){
return users.get();
}
public void clear(){
users.remove();
}
}
5.登录案例(前台代码)
- 写完了后台代码,估计还是不知道怎么用,我们看看前端代码就懂了
- index.html
- 可以看到这里,就是我们从拦截器中取到的东西,我们用这个东西,来判断用户每次请求是否登录了,然后改变头部的显示标志
- 如下图,未登录之前:
- 登录之后:
- 这样就完成了