拦截器(Interceptor)与过滤器(Filter)类似,是面向切面编程的一种具体实现。你可以使用拦截器执行某些任务,比如在控制器处理请求前记录日志、更新配置等。在 Spring 中,当请求发送到控制器时,在被控制器处理之前,它必须经过拦截器。

拦截器与过滤器的区别

拦截器(Interceptor)和过滤器(Filter)的主要区别在于作用范围和实现方式。

作用范围

  • 过滤器作用于整个 Web 应用程序,可以过滤所有请求和响应。它是 Servlet 规范的一部分,由 Servlet 容器管理。
  • 拦截器通常作用于特定框架。比如,在 Spring Boot 中,它主要拦截特定框架的请求处理流程,并在特定框架内处理请求。

实现方式

  • 过滤器实现 javax.servlet.Filter 接口,并在 web.xml 中或通过注解进行配置。需要实现 init()、doFilter() 和 destroy() 等方法。
  • 拦截器在不同框架中有不同的实现方式。比如,在 Spring MVC 中,可以实现 HandlerInterceptor 接口或继承 HandlerInterceptorAdapter 类。需要实现 preHandle()、postHandle() 和 afterCompletion() 等方法。

拦截器的作用

  1. 登录验证和访问控制:拦截器可用于检查用户的登录状态和权限,并根据需要执行相关处理。比如,可以使用拦截器验证用户的登录状态。如果未登录,则重定向到登录页面或返回相应的错误信息。
  2. 异常处理和统一错误处理:拦截器可以捕获并处理请求处理过程中发生的异常。可以根据异常类型执行适当的处理,比如返回自定义错误页面或错误信息,或执行特定的错误处理逻辑。当然,它还有许多其他应用场景,这里不再一一列举

自定义拦截器

自定义拦截器的话,需要实现HandlerInterceptor 接口或继承HandlerInterceptorAdapter 类,并且需要重写以下三个方法:

1.preHandle(HttpServletRequest request, HttpServletResponse response, Object handler):此方法在请求处理之前调用。该方法在拦截器类中首先执行,用于一些预初始化操作或对当前请求进行预处理。你还可以进行一些判断,以确定请求是否应该继续。此方法的返回值为布尔类型。当它返回 false 时,表示请求结束,后续的拦截器和控制器将不再执行。当它返回 true 时,将调用下一个拦截器的 preHandle 方法。如果已经是最后一个拦截器,则将调用当前请求的控制器方法。

2.postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView):此方法在当前请求处理完成后执行,即在控制器方法被调用之后。但是,它将在 DispatcherServlet 渲染视图之前被调用。因此,我们可以在此方法中在控制器处理后对 ModelAndView 对象进行操作。

3.afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handle, Exception ex):只有当相应拦截器类的 postHandle 方法的返回值为 true 时,此方法才会执行。顾名思义,此方法将在整个请求结束后执行,即在 DispatcherServlet 渲染相应视图之后。此方法主要用于资源清理。

用户登录权限验证

1.自定义拦截器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Component
public class LoginInterceptor implements HandlerInterceptor {
// 在调用目标方法之前执行的方法。
// 返回 true 表示拦截器验证成功,执行目标方法。
// 返回 false 表示拦截器验证失败,不执行后续业务逻辑。
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 用户登录判断业务。
HttpSession session = request.getSession(false);
if (session != null && session.getAttribute("session_userinfo") != null) {
//用户已登录
return true;
}
response.setStatus(401);
return false;
}
}

2.将自定义拦截器添加到系统配置中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Configuration
public class InterceptorConfig implements WebMvcConfigurer {
// 注入。
@Autowired
private LoginInterceptor loginInterceptor;

// 添加拦截器。
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(loginInterceptor)
.addPathPatterns("/**") // 拦截所有 URL。
.excludePathPatterns("/user/login") // 排除 URL:/user/login(登录)。
.excludePathPatterns("/user/reg") // 排除 URL:/user/reg(注册)。
.excludePathPatterns("/image/**") // 排除“image”文件夹下的所有文件。
.excludePathPatterns("/**/*.js") // 排除任意深度目录下的所有“.js”文件。
.excludePathPatterns("/**/*.css");
}
}

在配置类中,重写 addInterceptors 方法。此方法用于注册拦截器。在这里,通过调用 InterceptorRegistry 的 addInterceptor 方法添加拦截器,并设置拦截路径和排除路径。

具体来说,通过调用 addInterceptor(loginInterceptor) 添加 LoginInterceptor 拦截器。然后使用 addPathPatterns 方法指定需要拦截的 URL 路径模式。这里,“/**”用于表示拦截所有 URL。使用 excludePathPatterns 方法排除一些不会被拦截的特定 URL 路径。

3.用户控制器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@RestController
@RequestMapping("/user")
public class LoginTestController {

@PostMapping("/login")
public String login() {
return"login";
}
@PostMapping("/index")
public String index() {
return"index";
}
@PostMapping("/reg")
public String reg() {
return"reg";
}
}

分别访问3个接口的结果如下:
SpringBoot-Interceptor详解
SpringBoot-Interceptor详解
SpringBoot-Interceptor详解

添加统一访问前缀

在 WebMvcConfigurer 接口中,configurePathMatch 方法用于配置路径匹配规则。这里我们给所有请求地址添加前缀“pre”。

1
2
3
4
5
6
7
8
9
@Override
public void configurePathMatch(PathMatchConfigurer configurer) {
configurer.addPathPrefix("pre", new Predicate<Class<?>>() {
@Override
public boolean test(Class<?> aClass) {
return true;
}
});
}

在这个例子中,传递给 addPathPrefix 方法的前缀是“pre”,Predicate 对象是一个实现了 Predicate<Class<?>>接口的匿名内部类。Predicate 接口是 Java 8 中引入的一个函数式接口,其 test 方法用于判断传入的类是否满足条件。

在这个匿名内部类中,重写 test 方法使其始终返回 true,这意味着所有类都满足条件,并且将添加统一的访问前缀。

因此,通过这段代码的配置,所有请求路径都将在前面添加“pre”前缀。比如,如果原始路径是“example”,添加前缀后的路径变为“/pre/example”。这样可以实现对请求路径的统一处理。

注意:如果添加了前缀,拦截器的排除路径也应该相应更改。

1
2
3
4
5
6
7
8
9
10
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(loginInterceptor)
.addPathPatterns("/**") // 拦截所有 URL。
.excludePathPatterns("/pre/user/login") // 排除 URL:/user/login(登录)。
.excludePathPatterns("/pre/user/reg") // 排除 URL:/user/reg(注册)。
.excludePathPatterns("/pre/image/**") // 排除“image”文件夹下的所有文件。
.excludePathPatterns("/pre/**/*.js") // 排除任意深度目录下的所有“.js”文件。
.excludePathPatterns("/pre/**/*.css");
}

配置本地资源映射路径

实现 WebMvcConfigurer 接口并重写 addResourceHandlers(ResourceHandlerRegistry registry) 方法,其中:

  • addResourceHandler() 添加访问路径;
  • addResourceLocations() 添加映射的真实路径。映射的真实路径末尾必须跟“/”,否则无法映射。“/”在 Windows 和 Linux 中都适用。
1
2
3
4
5
6
7
8
9
10
11
@Value("${file.location}") // D:/test/
private String fileLocation;

@Value("${file.path}") // /file/**
private String filePath;

@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
// 当资源处理器匹配时,将 URL 映射到位置,即本地文件夹。
registry.addResourceHandler(filePath).addResourceLocations("file:///" + fileLocation); // 这里的最后一个“/”不能省略。
}

这段代码将配置一个拦截器。如果访问路径是 addResourceHandler 中的路径,那么它将被映射到 addResourceLocations 参数中的路径。这样,其他人就可以访问服务器上的本地文件,如本地图片或本地音乐视频。

参考文档
作者:写bug写bug
链接:https://juejin.cn/post/7499441782163898406
来源:稀土掘金