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

禁用Keycloak登录重定向后Spring Boot出现authenticationManager为空错误

问题:API网关Security配置添加自定义认证入口点后出现authenticationManager为空错误

问题背景

我在API网关的Security配置中禁用了Keycloak登录重定向(仅想在用户微服务中使用Keycloak),添加代码http.exceptionHandling().authenticationEntryPoint(new RedirectServerAuthenticationEntryPoint("/form-login"))后,出现如下错误:

Failed to instantiate [org.springframework.security.web.server.SecurityWebFilterChain]: Factory method 'springSecurityFilterChain' threw exception; nested exception is java.lang.IllegalArgumentException: authenticationManager cannot be null

相关代码

SecurityConfig类

@EnableGlobalMethodSecurity(prePostEnabled = true)
@Configuration
@EnableWebFluxSecurity
public class SecurityConfig extends KeycloakWebSecurityConfigurerAdapter  {


    @Bean
    public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) throws Exception {


        http
                .securityMatcher(new NegatedServerWebExchangeMatcher(
                        ServerWebExchangeMatchers.pathMatchers("/css/**","/contact-us","/actuator/**","/isalive/**",
                                "/api/v1/auth/**","/login", "/signup", "/publicOffers")))
                .authorizeExchange(exchanges -> exchanges.anyExchange().authenticated())
                .oauth2Login(Customizer.withDefaults())
                .oauth2ResourceServer()
                .jwt().jwtAuthenticationConverter(grantedAuthoritiesExtractor());

        http.exceptionHandling().authenticationEntryPoint(new RedirectServerAuthenticationEntryPoint("/form-login"))
                .and()
                .httpBasic();
        http.csrf().disable();
        return http.build();
    }

    private Converter<Jwt, Mono<AbstractAuthenticationToken>> grantedAuthoritiesExtractor() {
        GrantedAuthoritiesExtractor extractor = new GrantedAuthoritiesExtractor();
        return new ReactiveJwtAuthenticationConverterAdapter(extractor);
    }

    @Override
    protected SessionAuthenticationStrategy sessionAuthenticationStrategy() {
        return new RegisterSessionAuthenticationStrategy(new SessionRegistryImpl());

    }


    static class GrantedAuthoritiesExtractor extends JwtAuthenticationConverter {

        @Override
        protected Collection<GrantedAuthority> extractAuthorities(Jwt jwt) {
            Map<String, Object> claims = jwt.getClaims();
            JSONObject realmAccess = (JSONObject) claims.get("realm_access");
            if(realmAccess!=null){
                JSONArray roles = (JSONArray) realmAccess.get("roles");

                return roles.stream()
                        .map(Object::toString)
                        .map(SimpleGrantedAuthority::new)
                        .collect(Collectors.toSet());
            }
            return null;
        }
    }

    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
        KeycloakAuthenticationProvider keycloakAuthenticationProvider = keycloakAuthenticationProvider();
        keycloakAuthenticationProvider.setGrantedAuthoritiesMapper(new SimpleAuthorityMapper());
        auth.authenticationProvider(keycloakAuthenticationProvider);
    }
    @Override
    @Bean
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

}

application.properties配置

spring.security.oauth2.client.provider.keycloak.issuer-uri=http://localhost:8180/realms/myrealm
spring.security.oauth2.client.registration.spring-cloud-gateway-client.client-id=SpringTest-client
spring.security.oauth2.client.registration.spring-cloud-gateway-client.client-secret=E9Pk0mgRYThmvaq9wt38Spi7jpzkoc8l
spring.security.oauth2.client.registration.spring-cloud-gateway-client.provider=keycloak
spring.security.oauth2.client.registration.spring-cloud-gateway-client.authorization-grant-type=authorization_code
spring.security.oauth2.resourceserver.jwt.issuer-uri=http://localhost:8180/realms/myrealm

需求

希望修复该错误,并了解如何修改由spring.security.oauth2.client提供的authenticationManager Bean。


解决方案

1. 错误原因分析

添加httpBasic()配置后,WebFlux环境下的HttpBasicSpec需要关联ReactiveAuthenticationManager实例,但当前配置未正确提供该Bean,导致authenticationManager为空。另外,你混合使用了Servlet环境的KeycloakWebSecurityConfigurerAdapter和WebFlux的SecurityWebFilterChain,两者适配环境冲突,这是AuthenticationManager无法正确注入的核心原因。

2. 修复步骤

步骤1:移除Servlet环境的Keycloak适配代码

删除extends KeycloakWebSecurityConfigurerAdapter,同时移除以下Servlet相关配置方法:

  • configureGlobal(AuthenticationManagerBuilder auth)
  • authenticationManagerBean()
  • sessionAuthenticationStrategy()

步骤2:添加WebFlux环境的ReactiveAuthenticationManager

创建Reactive版本的认证管理器Bean,用于HttpBasic认证(示例为模拟逻辑,需替换为真实用户校验逻辑):

@Bean
public ReactiveAuthenticationManager reactiveAuthenticationManager() {
    return authentication -> {
        // 此处替换为对接用户微服务的认证逻辑
        if ("user".equals(authentication.getPrincipal()) && "password".equals(authentication.getCredentials())) {
            return Mono.just(new UsernamePasswordAuthenticationToken(
                    authentication.getPrincipal(),
                    authentication.getCredentials(),
                    Collections.singletonList(new SimpleGrantedAuthority("ROLE_USER"))
            ));
        }
        return Mono.error(new BadCredentialsException("Invalid credentials"));
    };
}

步骤3:调整SecurityWebFilterChain配置

将httpBasic()关联到上述ReactiveAuthenticationManager,并简化配置结构:

@Bean
public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http, ReactiveAuthenticationManager reactiveAuthenticationManager) throws Exception {
    http
            .securityMatcher(new NegatedServerWebExchangeMatcher(
                    ServerWebExchangeMatchers.pathMatchers("/css/**","/contact-us","/actuator/**","/isalive/**",
                            "/api/v1/auth/**","/login", "/signup", "/publicOffers")))
            .authorizeExchange(exchanges -> exchanges.anyExchange().authenticated())
            .oauth2Login(Customizer.withDefaults())
            .oauth2ResourceServer(oauth2 -> oauth2.jwt(jwt -> jwt.jwtAuthenticationConverter(grantedAuthoritiesExtractor())))
            .exceptionHandling(exceptions -> exceptions
                    .authenticationEntryPoint(new RedirectServerAuthenticationEntryPoint("/form-login")))
            .httpBasic(basic -> basic.authenticationManager(reactiveAuthenticationManager))
            .csrf().disable();
    return http.build();
}

步骤4:优化权限提取器(避免空指针)

原GrantedAuthoritiesExtractor返回null会触发异常,改为返回空集合:

static class GrantedAuthoritiesExtractor extends JwtAuthenticationConverter {
    @Override
    protected Collection<GrantedAuthority> extractAuthorities(Jwt jwt) {
        Map<String, Object> claims = jwt.getClaims();
        JSONObject realmAccess = (JSONObject) claims.get("realm_access");
        if(realmAccess != null){
            JSONArray roles = (JSONArray) realmAccess.get("roles");
            return roles.stream()
                    .map(Object::toString)
                    .map(SimpleGrantedAuthority::new)
                    .collect(Collectors.toSet());
        }
        return Collections.emptySet();
    }
}

3. 修改spring.security.oauth2.client相关认证逻辑说明

WebFlux环境下,spring.security.oauth2.client自动配置的是ReactiveOAuth2AuthorizedClientManager而非传统AuthenticationManager。如需自定义客户端认证逻辑,可通过以下方式调整:

注册自定义ReactiveOAuth2AuthorizedClientManager覆盖默认配置:

@Bean
public ReactiveOAuth2AuthorizedClientManager authorizedClientManager(
        ReactiveClientRegistrationRepository clientRegistrationRepository,
        ServerOAuth2AuthorizedClientRepository authorizedClientRepository) {

    ReactiveOAuth2AuthorizedClientProvider authorizedClientProvider =
            ReactiveOAuth2AuthorizedClientProviderBuilder.builder()
                    .authorizationCode()
                    .refreshToken()
                    .build();

    DefaultReactiveOAuth2AuthorizedClientManager authorizedClientManager =
            new DefaultReactiveOAuth2AuthorizedClientManager(
                    clientRegistrationRepository, authorizedClientRepository);
    authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);

    return authorizedClientManager;
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.16 03:50:39