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

如何在Spring Boot应用中集成双Spring Security认证并支持切换

解决方案:实现两种认证方式的配置切换与共存

错误原因分析

你遇到的IllegalStateException是因为Spring Security不允许同时使用旧版的WebSecurityConfigurerAdapter配置方式和新版的SecurityFilterChain配置方式。Azure AD的starter在Spring Boot 2.7.x版本中默认会自动注册一个WebSecurityConfigurerAdapter实例,而你自己的Documentum认证使用了SecurityFilterChain,两者冲突导致启动失败。

具体实现步骤

1. 整合所有依赖

保留两种认证所需的Maven依赖,注意Azure AD starter版本要与Spring Boot 2.7.3匹配(推荐4.4.0):

<!-- Documentum 认证依赖 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
    <version>2.7.3</version>
</dependency>

<!-- Azure AD SSO 依赖 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-oauth2-client</artifactId>
    <version>2.7.3</version>
</dependency>
<dependency>
    <groupId>com.azure.spring</groupId>
    <artifactId>spring-cloud-azure-starter-active-directory</artifactId>
    <version>4.4.0</version>
</dependency>

2. 添加配置开关

在application.properties中新增认证类型开关,同时保留原有配置:

# 认证类型开关:可选值 documentum / azure-ad,默认启用documentum
auth.type=documentum

# Documentum 相关配置(按需补充)
# documentum.repo-url=xxx

# Azure AD 配置
spring.cloud.azure.active-directory.enabled=false
spring.cloud.azure.active-directory.profile.tenant-id=xxxxxxxxxxxx
spring.cloud.azure.active-directory.credential.client-id=yyyyyyyyyyyy
spring.cloud.azure.active-directory.credential.client-secret=zzzzzzzzzz

3. 禁用Azure AD的自动配置

在应用启动类中排除Azure AD自动注册的WebSecurityConfigurerAdapter,避免冲突:

@SpringBootApplication(exclude = { AadWebSecurityConfigurerAdapterAutoConfiguration.class })
public class YourApplication {
    public static void main(String[] args) {
        SpringApplication.run(YourApplication.class, args);
    }
}

4. 拆分两种认证的配置类

用@ConditionalOnProperty注解实现配置的条件加载,确保同一时间只有一种认证方式生效。

Documentum认证配置类
@Configuration
@EnableWebSecurity
@ConditionalOnProperty(name = "auth.type", havingValue = "documentum", matchIfMissing = true)
public class DocumentumSecurityConfig {

    @Bean
    public AuthenticationProvider authenticationProvider() {
        return new DocumentumAuthenticationProvider();
    }

    @Bean
    public SecurityFilterChain documentumSecurityFilterChain(HttpSecurity http) throws Exception {
        http
                .authorizeRequests()
                .anyRequest().authenticated()
                .and()
                .formLogin()
                .loginPage("/content/login")
                .permitAll()
                .and()
                .logout()
                .logoutUrl("/content/logout")
                .logoutSuccessUrl("/content/logout")
                .permitAll();
        return http.build();
    }

    @Bean
    public WebSecurityCustomizer webSecurityCustomizer() {
        return (web) -> web.ignoring().antMatchers("/content/css/**" ,"/content/js/**" ,"/content/images/**" ,"/error/**");
    }

    @Bean
    public BCryptPasswordEncoder encodePWD() {
        return new BCryptPasswordEncoder();
    }
}
Azure AD认证配置类

用新版SecurityFilterChain实现Azure AD的SSO配置,替代自动生成的WebSecurityConfigurerAdapter:

@Configuration
@EnableWebSecurity
@ConditionalOnProperty(name = "auth.type", havingValue = "azure-ad")
public class AzureAdSecurityConfig {

    private final AadOAuth2ClientProperties aadProps;

    public AzureAdSecurityConfig(AadOAuth2ClientProperties aadProps) {
        this.aadProps = aadProps;
    }

    @Bean
    public SecurityFilterChain azureAdSecurityFilterChain(HttpSecurity http) throws Exception {
        http
                .authorizeRequests()
                .anyRequest().authenticated()
                .and()
                .oauth2Login(oauth2 -> oauth2
                        .clientRegistrationRepository(clientRegistrationRepository())
                        .authorizedClientService(authorizedClientService())
                )
                .logout(logout -> logout
                        .logoutSuccessUrl("/")
                        .permitAll()
                );
        return http.build();
    }

    @Bean
    public ClientRegistrationRepository clientRegistrationRepository() {
        return new InMemoryClientRegistrationRepository(azureAdClientRegistration());
    }

    @Bean
    public OAuth2AuthorizedClientService authorizedClientService(ClientRegistrationRepository repo) {
        return new InMemoryOAuth2AuthorizedClientService(repo);
    }

    private ClientRegistration azureAdClientRegistration() {
        String tenantId = aadProps.getProfile().getTenantId();
        return ClientRegistration.withRegistrationId("azure-ad")
                .clientId(aadProps.getCredential().getClientId())
                .clientSecret(aadProps.getCredential().getClientSecret())
                .scope("openid", "profile", "email")
                .authorizationUri("https://login.microsoftonline.com/" + tenantId + "/oauth2/v2.0/authorize")
                .tokenUri("https://login.microsoftonline.com/" + tenantId + "/oauth2/v2.0/token")
                .userInfoUri("https://graph.microsoft.com/oidc/userinfo")
                .userNameAttributeName(IdTokenClaimNames.SUB)
                .clientName("Azure AD")
                .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
                .redirectUri("{baseUrl}/login/oauth2/code/{registrationId}")
                .build();
    }

    @Bean
    public WebSecurityCustomizer webSecurityCustomizer() {
        return (web) -> web.ignoring().antMatchers("/content/css/**" ,"/content/js/**" ,"/content/images/**" ,"/error/**");
    }
}

切换认证方式

只需修改application.properties中的auth.type值:

  • 设置为documentum(或留空):启用自定义Documentum表单认证
  • 设置为azure-ad:启用Azure AD单点登录

修改后重启应用即可生效。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.19 12:01:04