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

如何移除Spring Authorization Server默认AuthenticationProviders

移除Spring Authorization Server默认AuthenticationProvider的解决方案

问题场景

在Spring Boot后端使用Spring Authorization Server实现App的OAuth2认证时,自定义了OAuth2RefreshTokenAuthProvider,加入了device_id校验逻辑:当请求中的device_id与存储的不匹配时,抛出OAuth2AuthenticationException。但框架默认的OAuth2RefreshTokenAuthenticationProvider仍会被加载,导致自定义Provider抛异常后,默认Provider继续执行,触发空指针错误:

java.lang.IllegalArgumentException: value cannot be null
    at org.springframework.util.Assert.notNull(Assert.java:181)
    at org.springframework.security.oauth2.server.authorization.token.OAuth2TokenContext$AbstractBuilder.put(OAuth2TokenContext.java:219)
    at org.springframework.security.oauth2.server.authorization.token.OAuth2TokenContext$AbstractBuilder.principal(OAuth2TokenContext.java:152)
    at org.springframework.security.oauth2.server.authorization.authentication.OAuth2RefreshTokenAuthenticationProvider.authenticate(OAuth2RefreshTokenAuthenticationProvider.java:171)
    at org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:182)
    at org.springframework.security.authentication.ObservationAuthenticationManager.lambda$authenticate$1(ObservationAuthenticationManager.java:54)
    at io.micrometer.observation.Observation.observe(Observation.java:564)
    at org.springframework.security.authentication.ObservationAuthenticationManager.authenticate(ObservationAuthenticationManager.java:53)

核心原因

ProviderManager的认证逻辑是:遍历所有支持当前认证类型的Provider,仅在认证成功或抛出AccountStatusException/InternalAuthenticationServiceException时停止遍历;抛出其他AuthenticationException(如OAuth2AuthenticationException)时,会继续执行后续Provider。

此时getProviders()会返回两个支持OAuth2RefreshTokenAuthenticationToken的Provider:自定义的和默认的。自定义Provider先执行并抛异常后,默认Provider仍会运行,由于上下文已被异常影响出现空值,最终触发报错。

解决方案

通过自定义AuthenticationManager并替换Spring Authorization Server默认的实例,仅加入自己的自定义Provider,彻底排除默认Provider。

步骤1:创建自定义AuthenticationManager Bean

@Bean
public AuthenticationManager customAuthenticationManager(
    OAuth2PasswordGrantAuthProvider passwordAuthProvider,
    OAuth2RefreshTokenAuthProvider refreshTokenAuthProvider
) {
    return new ProviderManager(Arrays.asList(
        passwordAuthProvider,
        refreshTokenAuthProvider
    ));
}

步骤2:在OAuth2配置中使用自定义AuthenticationManager

修改原oAuth2FilterChain方法,将自定义的AuthenticationManager注入并配置到OAuth2AuthorizationServerConfigurer中:

@Configuration
public class OAuth2Config {
    @Bean
    public SecurityFilterChain oAuth2FilterChain(
        HttpSecurity http,
        AuthenticationManager customAuthenticationManager,
        PasswordGrantAuthenticationConverter passwordGrantAuthenticationConverter,
        RefreshTokenAuthenticationConverter refreshTokenAuthenticationConverter
    ) throws Exception {
        OAuth2AuthorizationServerConfigurer configurer =
            new OAuth2AuthorizationServerConfigurer();

        configurer
            // 替换默认AuthenticationManager,仅使用自定义Provider
            .authenticationManager(customAuthenticationManager)
            .tokenEndpoint(token -> token
                .accessTokenRequestConverter(
                    new DelegatingAuthenticationConverter(List.of(
                        passwordGrantAuthenticationConverter,
                        refreshTokenAuthenticationConverter
                    )))
            );

        http
            .securityMatcher("/oauth2/**")
            .with(configurer, (authorizationServer) ->
                authorizationServer.oidc(Customizer.withDefaults()))
            .authorizeHttpRequests(
                auth -> auth
                    .requestMatchers("/oauth2/token").permitAll()
                    .anyRequest().authenticated())
            .csrf(csrf -> csrf.ignoringRequestMatchers("/oauth2/**"));

        return http.build();
    }

    // 以下原有Bean配置保持不变
    @Bean
    public RegisteredClientRepository registeredClientRepository(
        OAuth2Properties oAuth2Properties
    ) {
        TokenSettings tokenSettings = TokenSettings.builder()
            .accessTokenTimeToLive(Duration.ofMinutes(
                oAuth2Properties.getFirstScope().getAccessTokenTimeToLiveInMinutes()))
            .refreshTokenTimeToLive(Duration.ofDays(
                oAuth2Properties.getFirstScope().getRefreshTokenTimeToLiveInDays()))
            .reuseRefreshTokens(false)
            .build();
        RegisteredClient firstClient = RegisteredClient.withId(
                UUID.randomUUID().toString())
            .clientId(MyOAuth2.FIRST_CLIENT_ID)
            .clientAuthenticationMethod(ClientAuthenticationMethod.NONE)
            .authorizationGrantType(AuthorizationGrantType.PASSWORD)
            .authorizationGrantType(AuthorizationGrantType.REFRESH_TOKEN)
            .scope(MyOAuth2.FIRST_SCOPE)
            .tokenSettings(tokenSettings)
            .build();
        return new InMemoryRegisteredClientRepository(firstClient);
    }

    @Bean
    public OAuth2AuthorizationService authorizationService(
        JdbcTemplate jdbcTemplate,
        RegisteredClientRepository registeredClientRepository
    ) {
        return new JdbcOAuth2AuthorizationService(
            jdbcTemplate, registeredClientRepository);
    }
}

原理说明

Spring Authorization Server的OAuth2AuthorizationServerConfigurer默认会自动注册一系列默认的AuthenticationProvider。通过显式指定authenticationManager(),我们完全接管了Provider的管理,只加载自己需要的自定义Provider,从而避免默认Provider干扰业务逻辑。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.13 05:07:06