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

Spring Boot配置双Spring Security类实现Basic与JWT认证启动报错如何解决?

问题原因与解决方案

1. 可行性说明

Spring Security完全支持多套安全配置分别保护不同路径的接口,你的实现思路是正确的。

2. 启动报错原因

两个WebSecurityConfigurerAdapter实现类都未显式指定@Order注解,Spring Security对该类的默认排序值为100,排序值重复导致上下文初始化失败。

3. 额外隐藏问题

即使解决排序问题,你当前的配置也无法正常实现两种认证逻辑隔离:优先级更高的配置会默认拦截所有请求,导致第二个配置完全不生效。

4. 具体修改步骤

步骤1:给两个配置类设置不同优先级

给路径更具体的Basic认证配置设置更高优先级(Order值越小优先级越高),JWT配置设置更低优先级:

  • Basic认证配置类加@Order(1)
  • JWT认证配置类加@Order(2)

步骤2:限制Basic认证配置的生效范围

在Basic认证的configure(HttpSecurity http)方法开头添加antMatcher,限制该配置仅处理指定路径的请求,剩余请求交给下一个优先级的JWT配置处理。

步骤3:解决重复Bean冲突

两个配置类都定义了PasswordEncoder、UserDetailsService,会导致Bean冲突,可将公共的PasswordEncoder抽为独立公共Bean,给Basic认证专用的UserDetailsService添加@Qualifier做区分。

5. 修改后代码示例

Basic认证配置类

@Configuration
@EnableWebSecurity
@Order(1)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
    private static String REALM = "REAME";
    private static final String BASIC_AUTH_PATH = "/api/user/email/**";

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.antMatcher(BASIC_AUTH_PATH)
                   .csrf().disable()
                   .authorizeRequests()
                   .anyRequest().hasAnyRole("USER","ADMIN")
                   .and()
                   .httpBasic().realmName(REALM).authenticationEntryPoint(getBasicAuthEntryPoint()).and()
                   .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
    }
    
    @Bean
    public AuthEntryPoint getBasicAuthEntryPoint()
    {
        return new AuthEntryPoint();
    }

    @Override
    public void configure(WebSecurity web) 
    {
        web.ignoring().antMatchers(HttpMethod.OPTIONS, "/**");
    }

    @Bean
    @Qualifier("basicAuthUserDetails")
    @Override
    public UserDetailsService userDetailsService()
    {
        UserBuilder users = User.builder();
        InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
        BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
        manager.createUser(users
                .username("ReadUser")
                .password(encoder.encode("BimBumBam_2018"))
                .roles("USER").build());
        manager.createUser(users
                .username("Admin")
                .password(encoder.encode("MagicaBula_2018"))
                .roles("USER", "ADMIN").build());
        return manager;
    }
}

公共安全配置类

@Configuration
public class CommonSecurityConfig {
    @Bean
    public BCryptPasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
}

JWT认证配置类

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
@Order(2)
public class JWTWebSecurityConfig extends WebSecurityConfigurerAdapter {
    
    @Autowired
    private JwtUnAuthorizedResponseAuthenticationEntryPoint jwtUnAuthorizedResponseAuthenticationEntryPoint;
    
    @Autowired
    @Qualifier("customUserDetailsService")
    private UserDetailsService userDetailsService;
    
    @Autowired
    private JwtTokenAuthorizationOncePerRequestFilter jwtAuthenticationTokenFilter;
    
    @Autowired
    private BCryptPasswordEncoder passwordEncoder;
    
    @Value("${sicurezza.uri}")
    private String authenticationPath;
    
    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception 
    {
        auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder);
    }

    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception 
    {
        return super.authenticationManagerBean();
    }
    
    private static final String[] NOAUTH_MATCHER = {"/api/articoli/noauth/**"};
    private static final String[] USER_MATCHER = { "/api/users/jwttest"};
    private static final String[] ADMIN_MATCHER = { "/api/users/jwttest" };
    
    @Override
    protected void configure(HttpSecurity httpSecurity) throws Exception 
    {
        httpSecurity.csrf().disable()
        .exceptionHandling().authenticationEntryPoint(jwtUnAuthorizedResponseAuthenticationEntryPoint)
        .and()
        .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
        .and()
        .authorizeRequests()
        .antMatchers(NOAUTH_MATCHER).permitAll()
        .antMatchers(USER_MATCHER).hasAnyRole("USER")
        .antMatchers(ADMIN_MATCHER).hasAnyRole("ADMIN")
        .anyRequest().authenticated();
        
        httpSecurity.addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);
        
        httpSecurity.headers().frameOptions()
        .sameOrigin().cacheControl();  
        
    }
    
    @Override
    public void configure(WebSecurity webSecurity) 
    {
        webSecurity.ignoring()
        .antMatchers(HttpMethod.POST, authenticationPath)
        .antMatchers(HttpMethod.OPTIONS, "/**")
        .and().ignoring()
        .antMatchers(HttpMethod.GET, "/");  
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.24 19:36:03