一、前言
再说拦截器 之前,先讲讲过滤器 ,想必我们对过滤器是非常熟悉的,在Servlet里面的web.xml里面的<filter>标签 。那么许多人老是被这两个东西搞得晕头转向的。这里我举个例子说明他俩的区别:
- 过滤器:当你有一堆东西的时候,你只希望选择符合你要求的某一些东西。定义这些要求的工具,就是过滤器。
就好比在一堆沙子中淘出金子,把沙子过滤掉 - 拦截器:当你有了金子,准备高兴兴的准备回家娶媳妇,路上突然遇到了强盗
- 情况一:必须忍痛割爱,把金子给他,但是你又于心不忍,于是偷偷藏了一些,仅存的金子又分出一些给了强盗,这算保证住了小命。
- 情况二:你坚决不给,于是强盗把你杀了,金子拿了,最后人才两矢。
小结 过滤器 就是直接pass大多数的,留下一部分的精英 拦截器 就是想尽办法在从这些精英中在阻拦你前进,甚至是不让你继续通过,能通过的一定是最强的。
所以过滤器一定是在拦截器之前执行
一、spring中的拦截器
在web开发中,拦截器是经常用到的功能。它可以帮我们验证是否登陆、过滤静态资源等。
在Spring中的拦截器分为了两种:
- HanlerInterceptor接口:拦截请求地址的拦截器
- MethodInterceptor接口:拦截方法的拦截器
1.1 HandlerInterceptor拦截器🔥
HandlerInterceptor是springMVC项目中的拦截器,它拦截的目标是请求的地址, 比MethodInterceptor先执行。
实现一个HandlerInterceptor拦截器可以直接实现HandlerInterceptor接口,也可以继承HandlerInterceptorAdapter类。
这两种方法殊途同归,其实HandlerInterceptorAdapter也就是声明了HandlerInterceptor接口中所有方法的默认实现,而我们在继承他之后只需要重写必要的方法。
下面我们来看下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 {
}
}
preHandler: 目标方法执行完成之前- postHanlder:目标方法执行完成之后
- afterComplete:页面渲染以后
一般而言,我们的登录拦截操做,等相关的拦截操做都可以定义在preHandler方法里面。
1.2 MethodInterceptor
MethodInterceptor 是AOP项目中的拦截器,它拦截的目标是方法。
实现MethodInterceptor拦截器大致也分为两种,一种是实现MethodInterceptor接口,另一种利用AspectJ的注解或配置。
让我们看看MethodInterceptor的源码:
public interface MethodInterceptor extends Interceptor {
Object invoke(MethodInvocation var1) throws Throwable;
}
这里我着重使用以下HandlerInterceptor
二、如何使用HandlerInterceptor
2.1 使用步骤
- 编写一个拦截器类,实现HandlerInterceptor接口,并实现preHandler方法(核心)
- 编写一个配置类,实现WebMvcConfigurer接口
- 实现接口里的addInterceptors方法
- 在方法里,将定义的拦截器类,注入到Spring容器里,并配置拦截规则
2.2 案例实战
老规矩,还是使用之前Thymeleaf阶段的一个登录进行演示
【前置工作】 controller层跳转逻辑
@GetMapping(value = {"/", "/login"})
public String loginPage() {
return "login";
}
@PostMapping(value = "/login")
public String main(User user, HttpSession session, Model model) {
if (!StringUtils.isEmpty(user.getUsername()) && user.getPassword().equalsIgnoreCase("123456")) {
session.setAttribute("loginUser", user);
return "redirect:/main.html";
} else {
model.addAttribute("msg", "账号密码错误");
return "login";
}
}
@GetMapping("/main.html")
public String mainPage(HttpSession session, Model model) {
return "main";
}
【步骤一】 编写一个LoginInterceptor类
@Slf4j
public class LoginInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
log.info("拦截的请求路径是{}"+request.getRequestURI());
HttpSession session = request.getSession();
Object loginUser = session.getAttribute("loginUser");
if (loginUser != null) {
return true;
}
request.setAttribute("msg", "请先登录");
request.getRequestDispatcher("/").forward(request,response);
return false;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {}
}
【步骤二】 将拦截器注入并配置拦截规则
@Configuration
public class MyConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LoginInterceptor())
.addPathPatterns("/**")
.excludePathPatterns("/")
.excludePathPatterns("/login")
.excludePathPatterns("/css/**","/fonts/**","/images/**","/js/**");
}
}
🔥🔥说明 :如果我们在addInterceptors的方法里面使用addPathPattern 添加拦截路径的时候,使用/** 的方式进行拦截请求,那么这也就将静态资源也拦截掉了,所以我们可以通过excludePathPattern 来排除想放行的资源
【拦截效果】
【日志输出拦截URI】
|