如何让Spring Security的MyCustomFilter仅作用于指定REST端点?
问题
我拥有多个REST控制器,其中包含/api/test端点。目前我实现了一个继承自OncePerRequestFilter的MyCustomFilter类,相关配置代码如下:
public class SecurityConfig { private final UserServiceImplementation userServiceImplementation; private final JwtAuthFilter jwtAuthFilter; private final TimingFilter timingFilter; private final ApiRateLimit apiRateLimit; private final MyCustomFilter myCustomFilter; @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } @Bean public AuthenticationManager authenticationManagerBean(AuthenticationConfiguration configuration) throws Exception { return configuration.getAuthenticationManager(); } @Bean public AuthenticationProvider authenticationProvider() { final DaoAuthenticationProvider provider = new DaoAuthenticationProvider(); provider.setUserDetailsService(userServiceImplementation); provider.setPasswordEncoder(passwordEncoder()); return provider; } @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http .cors(cors -> cors .configurationSource(request -> new CorsConfiguration().applyPermitDefaultValues())) .csrf(crsf -> crsf .disable()) .authorizeHttpRequests(authz -> authz .requestMatchers(HttpMethod.GET, "/v1/user/hotel").permitAll() .requestMatchers(HttpMethod.GET,"/api/test").permitAll() .anyRequest().permitAll()) .exceptionHandling( ex -> ex .authenticationEntryPoint(new JwtAuthenticationEntryPoint())) .sessionManagement(session -> session .sessionCreationPolicy(SessionCreationPolicy.STATELESS)); http.authenticationProvider(authenticationProvider()); http.addFilterBefore(apiRateLimit, UsernamePasswordAuthenticationFilter.class); http.addFilterBefore(timingFilter, UsernamePasswordAuthenticationFilter.class); http.addFilterBefore(jwtAuthFilter, UsernamePasswordAuthenticationFilter.class); http.addFilterBefore(myCustomFilter, UsernamePasswordAuthenticationFilter.class); return http.build(); } } @Component public class MyCustomFilter extends OncePerRequestFilter { @Override protected void doFilterInternal(HttpServletRequest request, jakarta.servlet.http.HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { System.out.println("My custom filter is called"); filterChain.doFilter(request, response); } }
当前访问/v1/user/hotels等其他端点时,MyCustomFilter也会被触发,但我希望该过滤器仅作用于/api/test这类指定端点,该如何解决这个问题?
解决方案
方法1:在过滤器内部控制路径匹配
直接在MyCustomFilter中判断请求路径,仅对目标路径执行过滤逻辑,其余路径直接放行:
@Component public class MyCustomFilter extends OncePerRequestFilter { @Override protected void doFilterInternal(HttpServletRequest request, jakarta.servlet.http.HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { String path = request.getRequestURI(); String method = request.getMethod(); // 仅对GET方法的/api/test路径执行过滤逻辑 if ("/api/test".equals(path) && HttpMethod.GET.name().equals(method)) { System.out.println("My custom filter is called"); } // 无论是否匹配,都继续执行后续过滤器链 filterChain.doFilter(request, response); } // 可选:重写shouldNotFilter方法,直接跳过非目标路径的请求,减少无效执行 @Override protected boolean shouldNotFilter(HttpServletRequest request) throws ServletException { String path = request.getRequestURI(); String method = request.getMethod(); return !("/api/test".equals(path) && HttpMethod.GET.name().equals(method)); } }
这种方法实现简单,适合仅需在单个过滤器内控制路径的场景。
方法2:通过多SecurityFilterChain拆分路径规则
Spring Security支持配置多个SecurityFilterChain,为不同路径分配独立的过滤器集合。你可以为/api/test单独创建一个过滤器链,仅在该链中添加MyCustomFilter:
@Configuration public class SecurityConfig { // 原有Bean定义:passwordEncoder、authenticationManagerBean不变 // 针对/api/test的过滤器链,优先级更高 @Bean @Order(1) public SecurityFilterChain apiTestFilterChain(HttpSecurity http, MyCustomFilter myCustomFilter) throws Exception { http .securityMatcher("/api/test") .cors(cors -> cors.configurationSource(request -> new CorsConfiguration().applyPermitDefaultValues())) .csrf(csrf -> csrf.disable()) .authorizeHttpRequests(authz -> authz .requestMatchers(HttpMethod.GET, "/api/test").permitAll()) .addFilterBefore(myCustomFilter, UsernamePasswordAuthenticationFilter.class); return http.build(); } // 处理其他所有路径的默认过滤器链 @Bean @Order(2) public SecurityFilterChain defaultFilterChain(HttpSecurity http, UserServiceImplementation userServiceImplementation, JwtAuthFilter jwtAuthFilter, TimingFilter timingFilter, ApiRateLimit apiRateLimit) throws Exception { http .cors(cors -> cors.configurationSource(request -> new CorsConfiguration().applyPermitDefaultValues())) .csrf(csrf -> csrf.disable()) .authorizeHttpRequests(authz -> authz .requestMatchers(HttpMethod.GET, "/v1/user/hotel").permitAll() .anyRequest().permitAll()) .exceptionHandling(ex -> ex.authenticationEntryPoint(new JwtAuthenticationEntryPoint())) .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)); http.authenticationProvider(authenticationProvider(userServiceImplementation)); http.addFilterBefore(apiRateLimit, UsernamePasswordAuthenticationFilter.class); http.addFilterBefore(timingFilter, UsernamePasswordAuthenticationFilter.class); http.addFilterBefore(jwtAuthFilter, UsernamePasswordAuthenticationFilter.class); return http.build(); } // 修改authenticationProvider为带参数形式,供两个链共用 @Bean public AuthenticationProvider authenticationProvider(UserServiceImplementation userServiceImplementation) { final DaoAuthenticationProvider provider = new DaoAuthenticationProvider(); provider.setUserDetailsService(userServiceImplementation); provider.setPasswordEncoder(passwordEncoder()); return provider; } }
注意通过@Order指定优先级,数字越小优先级越高,确保/api/test的规则先被匹配。
方法3:用FilterRegistrationBean指定拦截路径
如果过滤器无需介入Spring Security认证流程,可以通过FilterRegistrationBean直接注册并指定拦截路径:
首先移除MyCustomFilter上的@Component注解,避免自动全局注册:
public class MyCustomFilter extends OncePerRequestFilter { @Override protected void doFilterInternal(HttpServletRequest request, jakarta.servlet.http.HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { System.out.println("My custom filter is called"); filterChain.doFilter(request, response); } }
然后在SecurityConfig中注册该过滤器:
@Configuration public class SecurityConfig { // 原有Bean定义不变,注意不要再将myCustomFilter添加到Spring Security的过滤器链中 @Bean public FilterRegistrationBean<MyCustomFilter> customFilterRegistration() { FilterRegistrationBean<MyCustomFilter> registrationBean = new FilterRegistrationBean<>(); registrationBean.setFilter(new MyCustomFilter()); // 指定拦截路径,支持通配符如/api/** registrationBean.addUrlPatterns("/api/test"); // 可选:指定允许拦截的HTTP方法 registrationBean.addInitParameter("allowedMethods", "GET"); return registrationBean; } }
这种方式直接通过Servlet容器规则拦截路径,适合独立于Spring Security认证流程的过滤器。
内容的提问来源于stack exchange,提问作者StayCautionSawan
相关产品推荐
相关产品推荐

