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

Spring WebFlux Security集成Keycloak OAuth:权限校验失败问题排查

问题描述

我要通过Spring WebFlux Security基于Keycloak令牌实现用户认证授权,根据令牌解析出的角色访问微服务受保护端点。目前遇到的问题:

  • 令牌有效时能访问资源,但添加@PreAuthorize方法级校验后返回HTTP 401
  • 报错信息:o.s.s.w.s.a.AuthenticationWebFilter: Authentication failed: An Authentication object was not found in the SecurityContext
  • 已确认Keycloak JWT格式转换器会被调用并生成数据,但SecurityContext未填充Authentication对象

相关配置代码如下:

Spring WebFlux Security主配置

@Configuration
@EnableWebFluxSecurity
@EnableMethodSecurity(securedEnabled = true, jsr250Enabled = true)
public class SecurityConfig {

    @Bean
    public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
        http
            .csrf()
                .disable()
            .authorizeExchange()
                .pathMatchers("/login").permitAll()
            .anyExchange()
                .authenticated()
                .and()
            .oauth2ResourceServer()  
                .jwt()
                .jwtAuthenticationConverter(keycloakJwtAuthConverter());
        return http.build();
    }

    @Bean
    public KeycloakJwtAuthConverter keycloakJwtAuthConverter() {
        return new KeycloakJwtAuthConverter();
    }

}

Keycloak JWT格式转换器

public class KeycloakJwtAuthConverter
        implements Converter<Jwt, Mono<AbstractAuthenticationToken>> {

    @Override
    public Mono<AbstractAuthenticationToken> convert(Jwt jwt) {

        Collection<GrantedAuthority> authorities = new ArrayList<>();
        authorities.add(new SimpleGrantedAuthority("ROLE_USER"));
        authorities.add(new SimpleGrantedAuthority("USER"));
        var newToken = new JwtAuthenticationToken(jwt, authorities, "myuser");

        return Mono.just(newToken);
    }
}

控制器代码

@RestController
@RequestMapping("/")
public class IndexController {

    @PreAuthorize("hasAnyAuthority('ROLE_USER')")
    @GetMapping("/test")
    public Mono<String> greet(Mono<Principal> principal) {
        return principal
                .map(Principal::getName)
                .map(name -> String.format("Test, %s", name));
    }
}

Application.yaml配置

spring:
  security:
    oauth2:
      client:
        provider: 
          keycloak:
            issuer-uri: http://localhost:3000/realms/msapp
            user-name-attribute: name
        registration:
          keycloak:
            client-id: msapp
            authorization-grant-type: authorization_code
            scope: openid
      resourceserver:
        jwt:
          issuer-uri: http://localhost:3000/realms/msapp
          jwks-set-uri: ${spring.security.oauth2.resourceserver.jwt.issuer-uri}/protocol/openid-connect/certs

问题原因及解决方案

1. JWT转换器接口实现错误

Spring WebFlux Security的oauth2ResourceServer().jwt().jwtAuthenticationConverter()要求的转换器是Converter<Jwt, ? extends AbstractAuthenticationToken>,而你当前实现的是Converter<Jwt, Mono<AbstractAuthenticationToken>>,这会导致Spring无法正确处理转换后的Authentication对象,进而无法将其放入SecurityContext中。

修正转换器代码:

public class KeycloakJwtAuthConverter implements Converter<Jwt, AbstractAuthenticationToken> {

    @Override
    public AbstractAuthenticationToken convert(Jwt jwt) {
        Collection<GrantedAuthority> authorities = new ArrayList<>();
        authorities.add(new SimpleGrantedAuthority("ROLE_USER"));
        authorities.add(new SimpleGrantedAuthority("USER"));
        return new JwtAuthenticationToken(jwt, authorities, "myuser");
    }
}

2. 确保方法级安全的上下文传播

在WebFlux反应式环境中,SecurityContext是通过Reactor Context传播的,修正转换器接口后,Spring会自动将转换后的Authentication对象放入Reactor Context,供@PreAuthorize注解校验时使用。

3. 可选:优化@EnableMethodSecurity配置

如果修正转换器后仍有问题,可以显式指定方法安全的代理模式,适配反应式环境:

@Configuration
@EnableWebFluxSecurity
@EnableMethodSecurity(
    securedEnabled = true, 
    jsr250Enabled = true,
    proxyTargetClass = true // 启用CGLIB代理
)
public class SecurityConfig {
    // 原有配置...
}

验证步骤

  1. 重启服务,携带有效Keycloak令牌访问/test端点
  2. 检查日志是否再出现SecurityContext无Authentication的报错
  3. 确认@PreAuthorize("hasAnyAuthority('ROLE_USER')")校验通过,返回正常响应

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.13 02:06:34