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

Spring Boot 3.1.0如何为特定路径配置带不同参数的过滤器

Spring Boot 3.1.0 配置路径专属JWT角色过滤器

你当前的代码中,两个AuthenticationFilter实例被全局添加到过滤器链中,没有限定生效路径,所以所有请求都会依次经过这两个过滤器,这就是/api/admins/get和/api/users/get都会触发两个过滤器执行的原因。以下是两种可行的解决方案:

方法一:使用FilterRegistrationBean注册路径专属过滤器

通过FilterRegistrationBean将过滤器注册为Spring Bean,并指定各自生效的URL模式,让过滤器只处理匹配的请求路径。

修改SecurityConfig配置

@RequiredArgsConstructor
@Configuration
@EnableWebSecurity
public class SecurityConfig {

    private final JwtService jwtService;

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
                .csrf(csrf -> csrf.disable())
                .cors(cors -> cors.disable())
                .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
                .authorizeHttpRequests(auth -> auth
                        .requestMatchers("/api/users/**", "/api/admins/**").authenticated()
                        .anyRequest().permitAll());

        return http.build();
    }

    // 注册用户路径的过滤器
    @Bean
    public FilterRegistrationBean<AuthenticationFilter> userAuthenticationFilter() {
        FilterRegistrationBean<AuthenticationFilter> registrationBean = new FilterRegistrationBean<>();
        registrationBean.setFilter(new AuthenticationFilter(jwtService, List.of("USER", "ADMIN")));
        registrationBean.addUrlPatterns("/api/users/**");
        // 设置过滤器顺序,确保在BasicAuthenticationFilter之前执行
        registrationBean.setOrder(Ordered.HIGHEST_PRECEDENCE + 1);
        return registrationBean;
    }

    // 注册管理员路径的过滤器
    @Bean
    public FilterRegistrationBean<AuthenticationFilter> adminAuthenticationFilter() {
        FilterRegistrationBean<AuthenticationFilter> registrationBean = new FilterRegistrationBean<>();
        registrationBean.setFilter(new AuthenticationFilter(jwtService, List.of("ADMIN")));
        registrationBean.addUrlPatterns("/api/admins/**");
        registrationBean.setOrder(Ordered.HIGHEST_PRECEDENCE + 1);
        return registrationBean;
    }
}

完善AuthenticationFilter的验证逻辑

@RequiredArgsConstructor
public class AuthenticationFilter implements Filter {

    private final JwtService jwtService;
    private final List<String> requiredRoles;

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        HttpServletRequest httpRequest = (HttpServletRequest) request;
        HttpServletResponse httpResponse = (HttpServletResponse) response;

        // 从请求头获取JWT令牌
        String authHeader = httpRequest.getHeader("Authorization");
        if (authHeader == null || !authHeader.startsWith("Bearer ")) {
            httpResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED, "缺少或无效的Authorization请求头");
            return;
        }

        String token = authHeader.substring(7);
        try {
            // 验证JWT并解析角色
            Claims claims = jwtService.extractAllClaims(token);
            List<String> userRoles = claims.get("roles", List.class);

            // 检查用户是否拥有至少一个所需角色
            boolean hasRequiredRole = userRoles.stream().anyMatch(requiredRoles::contains);
            if (!hasRequiredRole) {
                httpResponse.sendError(HttpServletResponse.SC_FORBIDDEN, "权限不足");
                return;
            }

            // 验证通过,继续执行后续过滤器
            chain.doFilter(request, response);
        } catch (JwtException e) {
            httpResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED, "无效的JWT令牌");
        }
    }
}

方法二:使用多SecurityFilterChain配置

Spring Boot 3支持多个SecurityFilterChain Bean,每个链通过securityMatcher匹配特定路径,独立配置对应的过滤器和授权规则。

@RequiredArgsConstructor
@Configuration
@EnableWebSecurity
public class SecurityConfig {

    private final JwtService jwtService;

    // 用户路径的SecurityFilterChain,优先级最高
    @Bean
    @Order(1)
    public SecurityFilterChain userSecurityFilterChain(HttpSecurity http) throws Exception {
        http
                .securityMatcher("/api/users/**")
                .csrf(csrf -> csrf.disable())
                .cors(cors -> cors.disable())
                .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
                .addFilterBefore(new AuthenticationFilter(jwtService, List.of("USER", "ADMIN")), BasicAuthenticationFilter.class)
                .authorizeHttpRequests(auth -> auth.anyRequest().authenticated());

        return http.build();
    }

    // 管理员路径的SecurityFilterChain
    @Bean
    @Order(2)
    public SecurityFilterChain adminSecurityFilterChain(HttpSecurity http) throws Exception {
        http
                .securityMatcher("/api/admins/**")
                .csrf(csrf -> csrf.disable())
                .cors(cors -> cors.disable())
                .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
                .addFilterBefore(new AuthenticationFilter(jwtService, List.of("ADMIN")), BasicAuthenticationFilter.class)
                .authorizeHttpRequests(auth -> auth.anyRequest().authenticated());

        return http.build();
    }

    // 其他路径的默认配置,优先级最低
    @Bean
    @Order(3)
    public SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {
        http
                .csrf(csrf -> csrf.disable())
                .cors(cors -> cors.disable())
                .authorizeHttpRequests(auth -> auth.anyRequest().permitAll());

        return http.build();
    }
}

两种方案均可解决问题:方法一适合过滤器逻辑独立且需要精准控制路径的场景;方法二更贴合Spring Security的链式配置风格,便于统一管理不同路径的安全规则。

内容的提问来源于stack exchange,提问作者Valentin Stamate

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.20 06:15:07