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

Keycloak与Spring集成时基于角色的认证失效问题排查

问题分析与解决方案

你的核心问题是:当前使用Spring Security OAuth2资源服务器模式(直接通过JWT令牌调用API),但配置的GrantedAuthoritiesMapper仅对OAuth2客户端登录流程生效,无法解析JWT中realm_access.roles的角色信息。

Spring Security中,GrantedAuthoritiesMapper用于处理OAuth2客户端登录(如浏览器跳转Keycloak登录后)的用户权限映射;而资源服务器模式下,JWT令牌的权限解析依赖JwtAuthenticationConverter——这就是你仅看到SCOPE_前缀权限、realm_access角色未被加载的原因。


解决步骤

1. 配置JWT权限转换器

添加JwtAuthenticationConverter Bean,专门从JWT的realm_access.roles字段提取角色,转换为Spring Security认可的GrantedAuthority(自动添加ROLE_前缀,匹配hasRole()规则):

@Bean
public JwtAuthenticationConverter jwtAuthenticationConverter() {
    JwtAuthenticationConverter converter = new JwtAuthenticationConverter();
    converter.setJwtGrantedAuthoritiesConverter(jwt -> {
        // 从JWT claims中获取realm_access对象
        Map<String, Object> realmAccess = (Map<String, Object>) jwt.getClaims()
                .getOrDefault("realm_access", new HashMap<>());
        // 提取roles列表,默认返回空集合
        Collection<String> roles = (Collection<String>) realmAccess.getOrDefault("roles", new HashSet<>());
        
        // 将每个角色转换为带ROLE_前缀的GrantedAuthority
        return roles.stream()
                .map(role -> new SimpleGrantedAuthority("ROLE_" + role))
                .collect(Collectors.toList());
    });
    return converter;
}

2. 修改资源服务器配置

在SecurityFilterChain中,将自定义转换器绑定到OAuth2资源服务器的JWT配置:

@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http, KeycloakLogoutHandler keycloakLogoutHandler) throws Exception {
    http.authorizeHttpRequests(auth -> auth
            .requestMatchers(new AntPathRequestMatcher("/api/v1/public/**")).permitAll()
            .requestMatchers(new AntPathRequestMatcher("/api/v1/auth/**")).permitAll()
            .requestMatchers(new AntPathRequestMatcher("/api/v1/admin/**")).hasRole("admin")
            .requestMatchers(new AntPathRequestMatcher("/api/v1/user/**")).hasRole("user")
            .anyRequest().authenticated()
    );
    // 替换默认JWT配置,绑定自定义转换器
    http.oauth2ResourceServer(oauth2 -> oauth2
            .jwt(jwt -> jwt.jwtAuthenticationConverter(jwtAuthenticationConverter())));
    http.oauth2Login(Customizer.withDefaults())
            .logout(logout -> logout.addLogoutHandler(keycloakLogoutHandler).logoutSuccessUrl("/"));

    http.csrf(AbstractHttpConfigurer::disable);
    http.cors(Customizer.withDefaults());

    return http.build();
}

3. 验证效果

重启服务后再次调用API,SecurityContextHolder.getContext().getAuthentication().getAuthorities()会显示ROLE_user、ROLE_admin等权限,hasRole("user")规则将正常生效。


额外说明

  • hasRole("user")会自动在权限前添加ROLE_前缀,因此转换器必须生成ROLE_user格式的权限才能匹配。
  • 如果Keycloak配置了客户端角色(resource_access.{client-id}.roles),可在转换器中添加对应解析逻辑,同时提取这部分角色。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.21 09:20:57