Spring Boot 七种拦截技术,横扫各种问题
环境:SpringBoot3.4.2
1. 简介
在 Spring Boot 中,拦截机制允许开发者在请求处理的不同阶段插入自定义逻辑,以实现统一处理、安全控制、日志记录等功能。主要的拦截机制包括:
- 过滤器(Filter):基于 Servlet 规范,拦截所有 HTTP 请求,处理原始数据(如编码、跨域),执行顺序最早。
- Spring Security:基于 Filter 链的安全框架,提供认证(OAuth2/JWT)和授权(RBAC)能力,适用于接口权限控制。
- 拦截器(Interceptor):作用于 Spring MVC 层,拦截 Controller 请求,适合权限校验、日志记录等场景。
- AOP(面向切面):拦截方法调用,适用于业务层增强(如事务管理、性能监控)。
- ControllerAdvice:全局处理 Controller 异常、数据绑定和参数预处理,如统一异常响应。
- RequestBodyAdvice/ResponseBodyAdvice:干预请求/响应体的序列化过程,适用于数据加解密或统一封装格式。
- REST Clients:调用Rest接口,拦截请求及响应。
这些机制覆盖了从底层数据流到业务逻辑的全流程拦截,开发者可根据需求灵活组合使用。
接下来,我将详细的介绍上面每一种拦截机制的使用。
2.实战案例
2.1 过滤器Filter
简介
过滤器基于Servlet规范,拦截所有HTTP请求,用于预处理和后处理,如编码设置、跨域处理等,是请求处理流程中的最前端。
执行位置
[浏览器] -> [Filter 1] -> [Filter 2] -> ... -> [Servlet容器]
示例
@WebFilter("/*")
public class DemoFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
System.err.println("filter before...") ;
chain.doFilter(request, response) ;
System.err.println("filter after...") ;
}
}
注意:我们需要使用@ServletComponentScan Servlet组件的扫描功能。
2.2 Spring Security过滤器
简介
Spring Security 的底层确实是通过 Filter 链(SecurityFilterChain)实现安全控制,例如认证、授权、CSRF 防护等。但它与直接使用原生 Filter 是有区别的:Spring Security实际只会创建一个FilterChainProxy(被注册到Servlet容器),这点与上面的过滤器一样,但是其内部包含了非常多的其它Filter,这些Filter并不会直接去拦截器请求,总结区别如下:
- 注册方式:原生 Filter 需要直接注册到 Servlet 容器中,而 Spring Security 的 Filter 是通过 FilterChainProxy 间接注册的。
- 管理方式:Spring Security 的 Filter 是在 FilterChainProxy 内部进行管理的,而不是直接暴露给 Servlet 容器。
- 灵活性:这种设计使得 Spring Security 能够更加灵活地配置和管理安全功能,例如根据请求路径、请求方法或其他条件来动态选择不同的 SecurityFilterChain。
执行位置
[浏览器] -> [其他Filters] -> [Spring Security Filter] -> [Servlet容器]
示例
该示例中,我们去添加一个自定义过滤器,此过滤器仅仅是被加入到了安全过滤器链中,并不会直接作用请求。
@Bean
SecurityFilterChain apiSecurity(HttpSecurity http) throws Throwable {
http.csrf(csrf -> csrf.disable()) ;
http.securityMatcher("/api/**", "/login") ;
http.formLogin(Customizer.withDefaults()) ;
http.authorizeHttpRequests(registry -> {
registry.anyRequest().authenticated() ;
}) ;
// 添加自定义的过滤器,此过滤器并非如上面一样直接作用于请求
http.addFilterBefore(new TokenFilter(), UsernamePasswordAuthenticationFilter.class) ;
return http.build() ;
}
// 自定义过滤器
public class TokenFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
System.err.println("token valid...") ;
filterChain.doFilter(request, response) ;
}
}
2.3 拦截器(Interceptor)
简介
拦截器作用于Spring MVC层,拦截Controller请求,用于权限校验、日志记录等,位于DispatcherServlet和Controller之间。
执行位置
[浏览器] -> [Filter链] -> [DispatcherServlet] -> [Interceptor] -> [Controller]
示例
// 1.编写拦截器
public class DemoInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.err.println("预处理...") ;
return true ;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
System.err.println("Controller执行完成之后...") ;
}
}
// 2.注册拦截器
@Component
public class WebConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new DemoInterceptor())
.addPathPatterns("/business/*") ;
}
}
// 3.Controller接口
@RestController
@RequestMapping("/business")
public class BusinessController {
@GetMapping("/index")
public ResponseEntity<?> index() {
System.err.println("执行Controller接口...") ;
return ResponseEntity.ok("business index...") ;
}
}
执行结果
2.4 AOP
简介
AOP拦截方法调用,实现业务逻辑的横切关注点,如事务管理、性能监控,通过切面编程增强业务逻辑。
执行位置
... -> [AOP切面] -> [目标方法]
示例
// 1.定义Aspect切面
@Aspect
@Component
public class LogAspect {
private static final Logger logger = LoggerFactory.getLogger("LogAspect") ;
@Around("execution(* com.pack.resp.service.BusinessService.*(..))")
public Object around(ProceedingJoinPoint pjp) throws Throwable {
logger.info("before log...") ;
Object ret = pjp.proceed() ;
logger.info("after log...") ;
return ret ;
}
}
// 2.编写业务方法(需要被上面切面拦截的)
@Service
public class BusinessService {
private static final Logger logger = LoggerFactory.getLogger("UserService") ;
public void save() {
logger.info("BusinessService save...") ;
}
}
// 3.Controller接口
@GetMapping("/save")
public ResponseEntity<?> save() {
logger.info("执行Controller#save接口...") ;
this.businessService.save() ;
return ResponseEntity.ok("business save...") ;
}
执行结果
2.5 ControllerAdvice
简介
全局处理Controller异常、数据绑定和参数预处理,如统一异常响应,提升代码可维护性和一致性。
执行位置
[ControllerAdvice定义的@InitBinder或@ModelAttribute] -> [Controller] -> [ControllerAdvice] (处理异常等)
示例
// 1.定义@ControllerAdvice
@RestControllerAdvice
public class GlobalControllerAdvice {
private static final Logger logger = LoggerFactory.getLogger("ControllerAdvice") ;
@ExceptionHandler(Exception.class)
public ResponseEntity<?> handle(Exception e) {
e.printStackTrace();
logger.info("@ExceptionHandler...") ;
return ResponseEntity.ok(e.getMessage()) ;
}
@ModelAttribute("info")
public Map<String, Object> info() {
logger.info("@ModelAttribute...") ;
return Map.of("name", "pack") ;
}
@InitBinder
public void binder(WebDataBinder binder) {
logger.info("@InitBinder...") ;
}
}
// 2.定义Controller
@GetMapping("/save")
public ResponseEntity<?> save(User user) {
logger.info("执行Controller#save接口...") ;
this.businessService.save() ;
return ResponseEntity.ok("business save...") ;
}
执行结果
2.6 RequestBodyAdvice/ResponseBodyAdvice
简介
干预请求/响应体的序列化过程,适用于数据加解密或统一封装格式,增强请求/响应的处理能力。
执行位置
[RequestBodyAdvice] (处理请求体) -> [Controller] -> [ResponseBodyAdvice] (处理响应体)
示例
// 1.请求处理器
@ControllerAdvice
public class RequestProcessor extends RequestBodyAdviceAdapter {
private static final Logger logger = LoggerFactory.getLogger("RequestProcessor") ;
public boolean supports(MethodParameter methodParameter, Type targetType,
Class<? extends HttpMessageConverter<?>> converterType) {
return true ;
}
public HttpInputMessage beforeBodyRead(HttpInputMessage inputMessage, MethodParameter parameter, Type targetType,
Class<? extends HttpMessageConverter<?>> converterType) throws IOException {
logger.info("RequestBodyAdvice before...") ;
return super.beforeBodyRead(inputMessage, parameter, targetType, converterType) ;
}
}
// 2.响应处理器
@ControllerAdvice
public class ResponseProcessor implements ResponseBodyAdvice<Object> {
private static final Logger logger = LoggerFactory.getLogger("ResponseProcessor") ;
public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
return true ;
}
public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType,
Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request,
ServerHttpResponse response) {
logger.info("ResponseBodyAdvice before...") ;
return body ;
}
}
// 3.定义Controller接口
@PostMapping("/create")
public ResponseEntity<?> create(@RequestBody User user) {
logger.info("执行Controller#create接口...") ;
return ResponseEntity.ok("business create...") ;
}
运行结果
注意:
RequestBodyAdvice如下场景有效:
- 方法参数有@RequestBody,@RequestPart注解
- 参数类型是HttpEntity,RequestEntity
ResponseBodyAdvice如下场景有效:
- 方法返回值有@ResponseBody注解
- 返回值是HttpEntity类型并且不是RequestEntity类型,或者是ErrorResponse类型,或者是ProblemDetail类型
2.7 REST Clients
简介
用于调用Rest接口,拦截请求及响应,可进行请求参数处理、响应数据解析等,实现微服务间的通信。
执行位置
[本系统] -> -> [REST Client] -> -> [外部Rest接口]
示例
// 1.定义拦截器
public class LoggerRequestInterceptor implements ClientHttpRequestInterceptor {
private static final Logger logger = LoggerFactory.getLogger("ClientHttpRequestInterceptor") ;
@Override
public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution)
throws IOException {
logger.info("Rest Client request before...") ;
ClientHttpResponse response = execution.execute(request, body) ;
logger.info("Rest Client response after...") ;
return response ;
}
}
// 2.配置Rest Clients
@Bean
RestTemplate restTemplate(RestTemplateBuilder builder) {
RestTemplate restTemplate = builder
.interceptors(new LoggerRequestInterceptor())
.build() ;
return restTemplate ;
}
@Bean
RestClient restClient(RestClient.Builder builder) {
RestClient restClient = builder
.requestInterceptor(new LoggerRequestInterceptor())
.build() ;
return restClient ;
}
// 3.接口调用
@GetMapping("/invoke")
public ResponseEntity<?> invoke() {
String url = "http://localhost:8080/business/index" ;
// String data = this.restTemplate.getForObject(url, String.class) ;
String data = this.restClient.get().uri(url).retrieve().body(String.class) ;
return ResponseEntity.ok(data) ;
}
执行结果
相关文章
- SpringBoot注解 & 拦截器 & 反射
- 如何批量获取指定歌手的音乐(批量搜索歌曲)
- 系列:第五篇—接口发生异常如何统一处理
- Spring Cloud Hystrix熔断与负载均衡深度解析:原理实践与避坑指南
- Spring WebFlux核心处理组件DispatcherHandler
- SpringCloud相关组件——健康监控!
- springboot中ServletComponentScan注解的作用
- JavaEE概述总结:Servlet生命周期+JSP内置对象
- Spring Boot3中解决跨域问题的五种常用方法?
- Java中拦截器和过滤器的比较(java 拦截器和过滤器)