一、前言

再说​​拦截器​​​之前,先讲讲​​过滤器​​​,想必我们对过滤器是非常熟悉的,在Servlet里面的web.xml里面的​​<filter>标签​​。那么许多人老是被这两个东西搞得晕头转向的。这里我举个例子说明他俩的区别:


  • 过滤器:当你有一堆东西的时候,你只希望选择符合你要求的某一些东西。定义这些要求的工具,就是过滤器。​​就好比在一堆沙子中淘出金子,把沙子过滤掉​
  • 拦截器:当你有了金子,准备高兴兴的准备回家娶媳妇,路上突然遇到了强盗
  • 情况一:必须忍痛割爱,把金子给他,但是你又于心不忍,于是偷偷藏了一些,仅存的金子又分出一些给了强盗,这算保证住了小命。
  • 情况二:你坚决不给,于是强盗把你杀了,金子拿了,最后人才两矢。

小结
​​过滤器​​ 就是直接pass大多数的,留下一部分的精英
​​拦截器

所以过滤器一定是在拦截器之前执行

SpringBoot拦截器-HandlerInterceptor接口_拦截器

一、spring中的拦截器

在web开发中,拦截器是经常用到的功能。它可以帮我们验证是否登陆、过滤静态资源等。

在Spring中的拦截器分为了两种:

  1. HanlerInterceptor接口:拦截请求地址的拦截器
  2. MethodInterceptor接口:拦截方法的拦截器

1.1 HandlerInterceptor拦截器🔥

HandlerInterceptor是springMVC项目中的拦截器,它拦截的目标是请求的地址,

实现一个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 使用步骤

  1. 编写一个拦截器类,实现HandlerInterceptor接口,并实现preHandler方法(核心)
  2. 编写一个配置类,实现WebMvcConfigurer接口
  3. 实现接口里的addInterceptors方法
  4. 在方法里,将定义的拦截器类,注入到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) {
// 这块可以写一些数据库账号密码的逻辑操作
// ...
// 如果密码账号无误,ok登录 把登录信息放到Session作用域中
// 这里只做非空判断
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 {

// logback日志输出获取拦截的URI
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 {

/**
* 配置拦截规则与注入拦截器
* @param registry
*/
@Override
public void addInterceptors(InterceptorRegistry registry) {
// addPathPattern 添加拦截规则 /** 拦截所有包括静态资源
// excludePathPattern 排除拦截规则 所以我们需要放开静态资源的拦截
registry.addInterceptor(new LoginInterceptor())
.addPathPatterns("/**")
.excludePathPatterns("/")
.excludePathPatterns("/login")
.excludePathPatterns("/css/**","/fonts/**","/images/**","/js/**");
}
}

🔥🔥说明:如果我们在addInterceptors的方法里面使用​addPathPattern​添加拦截路径的时候,使用​/**​的方式进行拦截请求,那么这也就将静态资源也拦截掉了,所以我们可以通过​excludePathPattern​来排除想放行的资源

【拦截效果】

SpringBoot拦截器-HandlerInterceptor接口_ide_02


【日志输出拦截URI】

SpringBoot拦截器-HandlerInterceptor接口_拦截器_03