Spring Boot 七种拦截技术,横扫各种问题

Spring Boot 七种拦截技术,横扫各种问题

编程文章jaq1232025-07-28 16:56:485A+A-

环境: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) ;
}

执行结果

点击这里复制本文地址 以上内容由jaq123整理呈现,请务必在转载分享时注明本文地址!如对内容有疑问,请联系我们,谢谢!

苍茫编程网 © All Rights Reserved.  蜀ICP备2024111239号-21