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

使用SecurityFilterChain实现/api/**端点支持JWT与账号密码双认证

问题:如何让/api/**端点同时支持JWT令牌和账号密码(Basic认证)访问?

我需要让我的/api/**端点可通过JWT令牌或账号密码两种方式访问。参考Spring官方文档后配置了两个SecurityFilterChain,但当前配置下API端点仅支持Basic认证,配置代码如下:

@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfiguration {

    static final int BCRYPT_PASSWORD_STRENGTH = 10;
    
    @Bean
    @Order(1)
    public SecurityFilterChain apiSecurityFilterChain(HttpSecurity http, @Qualifier("apiUserDetailsService") UserDetailsService userDetailsService) throws Exception {

        http.antMatcher("/api/**")
                .userDetailsService(userDetailsService)
                .csrf().disable()
                .cors(Customizer.withDefaults())
                .exceptionHandling(configurer -> configurer.authenticationEntryPoint(new AuthenticationFallbackEntryPoint()))
                .sessionManagement(configurer -> configurer.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
                .authorizeHttpRequests(authorize -> authorize.anyRequest().authenticated())
                .httpBasic(Customizer.withDefaults());

        return http.build();
    }

    @Bean
    @Order(2)
    public SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http, GrantedPortalRoleConverter grantedPortalRoleConverter) throws Exception {
        JwtAuthenticationConverter jwtAuthenticationConverter = new JwtAuthenticationConverter();
        jwtAuthenticationConverter.setJwtGrantedAuthoritiesConverter(grantedPortalRoleConverter);

        http
                .csrf().disable()
                .cors(Customizer.withDefaults())
                .exceptionHandling(configurer -> configurer.authenticationEntryPoint(new AuthenticationFallbackEntryPoint()))
                .sessionManagement(configurer -> configurer.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
                .authorizeHttpRequests(authorize -> authorize
                        .antMatchers(
                                "/swagger-ui/**",
                                "/swagger-ui.html",
                                "/swagger-resources/**",
                                "/v3/api-docs/**")
                        .permitAll()
                        .anyRequest().authenticated())
                .oauth2ResourceServer(configurer -> configurer.jwt().jwtAuthenticationConverter(jwtAuthenticationConverter));
        return http.build();
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder(BCRYPT_PASSWORD_STRENGTH);
    }   
}

请问如何调整该配置(仍使用SecurityFilterChain)以实现我的需求?


解决方案

问题出在你拆分了两个SecurityFilterChain,其中@Order(1)的apiSecurityFilterChain只配置了Basic认证,并且匹配所有/api/**请求,导致@Order(2)的JWT配置根本不会被触发。要让同一个端点支持多种认证方式,需要在同一个SecurityFilterChain中同时配置Basic和JWT两种认证机制。

调整后的配置如下:

@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfiguration {

    static final int BCRYPT_PASSWORD_STRENGTH = 10;
    
    @Bean
    @Order(1)
    public SecurityFilterChain apiSecurityFilterChain(HttpSecurity http, 
                                                     @Qualifier("apiUserDetailsService") UserDetailsService userDetailsService,
                                                     GrantedPortalRoleConverter grantedPortalRoleConverter) throws Exception {
        // 配置JWT转换器
        JwtAuthenticationConverter jwtAuthenticationConverter = new JwtAuthenticationConverter();
        jwtAuthenticationConverter.setJwtGrantedAuthoritiesConverter(grantedPortalRoleConverter);

        http.antMatcher("/api/**")
                .userDetailsService(userDetailsService)
                .csrf().disable()
                .cors(Customizer.withDefaults())
                .exceptionHandling(configurer -> configurer.authenticationEntryPoint(new AuthenticationFallbackEntryPoint()))
                .sessionManagement(configurer -> configurer.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
                .authorizeHttpRequests(authorize -> authorize.anyRequest().authenticated())
                // 同时启用Basic认证和JWT认证
                .httpBasic(Customizer.withDefaults())
                .oauth2ResourceServer(configurer -> configurer.jwt().jwtAuthenticationConverter(jwtAuthenticationConverter));

        return http.build();
    }

    @Bean
    @Order(2)
    public SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http, GrantedPortalRoleConverter grantedPortalRoleConverter) throws Exception {
        JwtAuthenticationConverter jwtAuthenticationConverter = new JwtAuthenticationConverter();
        jwtAuthenticationConverter.setJwtGrantedAuthoritiesConverter(grantedPortalRoleConverter);

        http
                .csrf().disable()
                .cors(Customizer.withDefaults())
                .exceptionHandling(configurer -> configurer.authenticationEntryPoint(new AuthenticationFallbackEntryPoint()))
                .sessionManagement(configurer -> configurer.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
                .authorizeHttpRequests(authorize -> authorize
                        .antMatchers(
                                "/swagger-ui/**",
                                "/swagger-ui.html",
                                "/swagger-resources/**",
                                "/v3/api-docs/**")
                        .permitAll()
                        .anyRequest().authenticated())
                .oauth2ResourceServer(configurer -> configurer.jwt().jwtAuthenticationConverter(jwtAuthenticationConverter));
        return http.build();
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder(BCRYPT_PASSWORD_STRENGTH);
    }   
}

关键调整说明:

  • 在apiSecurityFilterChain中同时添加httpBasic()和oauth2ResourceServer(jwt)配置,这样/api/**端点会同时支持两种认证方式
  • Spring Security会自动尝试所有配置的认证机制,只要其中一种验证通过就允许访问
  • 保持@Order顺序,确保/api/**的请求优先被第一个过滤器链处理,其他请求走第二个过滤器链

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.18 17:35:19