You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何让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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.30 18:13:10