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

WebFlux整合Keycloak 自定义JWT roles实现@PreAuthorize安全未生效

问题根因定位与修复方案

核心问题排查

  • 自定义权限转换器选错了技术栈实现类:你当前编写的GrantedAuthoritiesExtractor继承的是Servlet(WebMVC)栈下的JwtAuthenticationConverter,虽然外层套了ReactiveJwtAuthenticationConverterAdapter做适配,但因为链路配置的优先级问题,这套自定义逻辑根本没有被实际加载到请求处理链路中。
  • 自定义过滤链优先级低于Spring Boot自动配置的默认链:你当前定义的SecurityWebFilterChain没有显式设置优先级,Spring Boot的OAuth2自动配置会生成一套默认的过滤链,优先级高于你未声明顺序的自定义链。所有请求都会先被默认链拦截,使用默认的JWT权限转换器——这个默认转换器的内置逻辑就是提取JWT中的scope声明,拼接SCOPE_前缀作为权限项,这就是为啥你写@PreAuthorize("hasAuthority('SCOPE_email')")可以生效,而自定义的角色权限完全不生效。
  • 权限提取逻辑没有完全隔离默认行为:就算自定义链生效,你使用的Servlet版转换器默认会开启scope提取、SCOPE_前缀拼接的逻辑,没有清空默认前缀的情况下,你提取的业务角色也会被加上不必要的前缀,和方法级权限注解的匹配规则对不上。

具体修复步骤

1. 重写自定义权限转换器,直接使用WebFlux原生实现

替换原有基于Servlet栈的转换器,改用WebFlux专属的ReactiveJwtAuthenticationConverter实现,完全覆盖默认的scope提取逻辑、清空默认权限前缀:

@Bean
public Converter<Jwt, Mono<AbstractAuthenticationToken>> grantedAuthoritiesExtractor() {
    ReactiveJwtAuthenticationConverter converter = new ReactiveJwtAuthenticationConverter() {
        @Override
        protected Mono<Collection<GrantedAuthority>> extractAuthorities(Jwt jwt) {
            // 直接提取JWT顶层自定义roles数组,完全替代默认的scope提取逻辑
            Collection<?> customRoles = (Collection<?>) jwt.getClaims().getOrDefault("roles", Collections.emptyList());
            List<GrantedAuthority> authorities = customRoles.stream()
                    .map(Object::toString)
                    .map(SimpleGrantedAuthority::new)
                    .collect(Collectors.toList());
            return Mono.just(authorities);
        }
    };
    // 清空默认的SCOPE_前缀,避免业务角色被强制加前缀导致匹配失败
    converter.setAuthorityPrefix("");
    return converter;
}

2. 提升自定义过滤链优先级,覆盖自动配置的默认链

给自定义的SecurityWebFilterChain添加最高优先级注解,确保所有请求优先走自定义配置的链路,而不是自动生成的默认链:

@Bean
@Order(Ordered.HIGHEST_PRECEDENCE)
public SecurityWebFilterChain configure(ServerHttpSecurity http) {
    http
        .authorizeExchange()
        .anyExchange().authenticated()
        .and()
        .oauth2Login()
        .and()
        .oauth2ResourceServer()
        .jwt()
        .jwkSetUri(jwkSetUri)
        .jwtAuthenticationConverter(grantedAuthoritiesExtractor());
    return http.build();
}

3. 消除配置冲突

  • 删除冗余的ReactiveJwtDecoder Bean配置,统一使用jwkSetUri的配置方式,避免Java代码与application.yml中的资源服务器配置重复冲突。
  • 检查application.yml中resourceserver.jwt的配置,移除和Java代码重复的issuer-uri、jwk-set-uri配置,避免自动配置覆盖自定义的权限转换器逻辑。

验证方式

在自定义的extractAuthorities方法中添加日志,打印提取到的角色列表、最终生成的权限集合,确认请求被拦截时,认证对象中包含JWT顶层roles数组里的ROLE_ADMIN权限项,此时即可正常使用@PreAuthorize("hasAuthority('ROLE_ADMIN')")做方法级权限控制。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.24 04:36:04