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

Spring Boot集成Keycloak时Role Mapping失效,角色返回Null

Keycloak角色解析为Null问题排查与解决方案

1. 检查Keycloak客户端配置

  • 确认客户端的Client Scope已添加roles scope,并设置为默认或强制包含
  • 检查客户端Mappers配置:添加Realm Roles和Client Roles映射器,确保映射器的Token Claim Name与你在JwtAuthConverter中读取的字段一致(Keycloak默认 realm 角色存于realm_access.roles,客户端角色存于resource_access.{你的客户端ID}.roles)
  • 确保客户端Access Type设置为confidential(后端服务场景)

2. 修正JwtAuthConverter实现

Spring Boot 3.x适配Spring Security 6.x,需正确从JWT的指定Claim中提取角色,示例实现如下:

import org.springframework.core.convert.converter.Converter;
import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.oauth2.jwt.Jwt;
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken;
import org.springframework.security.oauth2.server.resource.authentication.JwtGrantedAuthoritiesConverter;

import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class JwtAuthConverter implements Converter<Jwt, AbstractAuthenticationToken> {

    private final JwtGrantedAuthoritiesConverter defaultConverter = new JwtGrantedAuthoritiesConverter();
    // 替换为你的Keycloak客户端ID
    private static final String CLIENT_ID = "your-client-id";
    private static final String REALM_ACCESS = "realm_access";
    private static final String RESOURCE_ACCESS = "resource_access";
    private static final String ROLES = "roles";

    @Override
    public AbstractAuthenticationToken convert(Jwt jwt) {
        Collection<GrantedAuthority> authorities = Stream.concat(
                defaultConverter.convert(jwt).stream(),
                extractRealmRoles(jwt).stream(),
                extractClientRoles(jwt).stream()
        ).collect(Collectors.toSet());

        return new JwtAuthenticationToken(jwt, authorities);
    }

    private Collection<? extends GrantedAuthority> extractRealmRoles(Jwt jwt) {
        List<String> roles = (List<String>) jwt.getClaimAsMap(REALM_ACCESS).getOrDefault(ROLES, List.of());
        return roles.stream()
                .map(role -> new SimpleGrantedAuthority("ROLE_" + role)) // Spring Security默认识别ROLE_前缀的角色
                .collect(Collectors.toList());
    }

    private Collection<? extends GrantedAuthority> extractClientRoles(Jwt jwt) {
        var resourceMap = jwt.getClaimAsMap(RESOURCE_ACCESS);
        if (resourceMap != null && resourceMap.containsKey(CLIENT_ID)) {
            List<String> roles = (List<String>) ((java.util.Map<?, ?>) resourceMap.get(CLIENT_ID)).getOrDefault(ROLES, List.of());
            return roles.stream()
                    .map(role -> new SimpleGrantedAuthority("ROLE_" + role))
                    .collect(Collectors.toList());
        }
        return List.of();
    }
}

若无需ROLE_前缀,可自定义JwtGrantedAuthoritiesConverter并设置setAuthorityPrefix("")

3. 配置Spring Security资源服务器

确保在Security配置中正确注册自定义的JwtAuthConverter:

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationConverter;
import org.springframework.security.web.SecurityFilterChain;

@Configuration
@EnableWebSecurity
public class SecurityConfig {

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
                .authorizeHttpRequests(auth -> auth
                        .anyRequest().authenticated()
                )
                .oauth2ResourceServer(oauth2 -> oauth2
                        .jwt(jwt -> jwt
                                .jwtAuthenticationConverter(jwtAuthConverter())
                        )
                );
        return http.build();
    }

    @Bean
    public JwtAuthenticationConverter jwtAuthConverter() {
        return new JwtAuthConverter();
    }
}

4. 验证Token内容

使用JWT解析工具查看Token Payload,确认是否包含realm_access、resource_access字段及对应的roles列表。若Token中无这些字段,需回到Keycloak客户端配置重新检查。

5. 确认依赖配置

确保项目依赖正确,无需额外引入Keycloak适配器,Spring Boot 3.x通过标准OAuth2资源服务器依赖即可支持Keycloak JWT:
Maven示例:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-oauth2-resource-server</artifactId>
</dependency>

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.13 21:55:18