使用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
相关产品推荐
相关产品推荐

