使用keycloak-spring-boot-starter时如何通过Keycloak Java API获取用户组等数据
问题核心原因
Keycloak Spring Boot适配器仅负责完成JWT访问令牌的合法性校验,所有能从KeycloakPrincipal、KeycloakSecurityContext中读取到的用户数据,本质都是已经被编码在JWT令牌声明(Claims)中的内容。
Keycloak默认颁发的访问令牌仅包含用户ID、用户名、邮箱这类核心基础字段,用户所属分组、自定义档案字段、映射角色这类信息默认不会写入令牌声明,因此你仅能拿到用户ID,并非适配器功能缺失。
最优实现:通过Keycloak令牌映射注入用户数据
不需要额外调用Admin接口,仅需在Keycloak管理台配置规则,将需要的用户数据直接写入访问令牌,即可在现有代码中直接读取,这也是Keycloak官方推荐的常规用户数据获取方案,性能优于服务端远程调用:
- 操作入口:Keycloak管理台 -> 对应
test域 -> 客户端 -> 选中你当前使用的spring-boot-client客户端 -> 进入【Mappers】配置页 - 针对需获取的信息创建对应映射器,确保所有映射器都勾选【Add to access token】开关:
- 获取用户所属分组:创建类型为
Group Membership的映射器,设置Token声明名称为groups,保存后所有颁发给该客户端的令牌都会携带用户全量分组路径信息 - 获取自定义用户档案字段(如工号、手机号):创建类型为
User Attribute的映射器,选中需要暴露的用户属性,设置对应声明名称即可 - 获取域/客户端角色:分别创建类型为
User Realm Role/User Client Role的映射器,勾选写入访问令牌
- 获取用户所属分组:创建类型为
- 配置完成后无需修改适配器逻辑,直接在现有接口中通过Token读取自定义声明即可,示例代码:
@GetMapping("/ping") public ResponseEntity<Map<String, Object>> ping(KeycloakPrincipal<KeycloakSecurityContext> principal) { IDToken token = principal.getKeycloakSecurityContext().getToken(); // 读取令牌中注入的分组数据 List<String> userGroups = (List<String>) token.getOtherClaims().get("groups"); // 读取令牌中注入的自定义用户属性 String userPhone = (String) token.getOtherClaims().get("phoneNumber"); return ResponseEntity.ok(Map.of( "userId", token.getSubject(), "groups", userGroups, "phone", userPhone )); }
现有配置修正
你的YAML配置存在逻辑冲突与拼写错误,会影响部分功能的正常运行:
- 配置中同时设置
public-client: true、客户端secret、bearer-only: true,三者逻辑矛盾:public-client: true代表无密钥的公共客户端,与你配置的客户端secret不兼容,需将public-client改为false,匹配你服务作为纯资源服务器的定位。 - 权限配置存在拼写错误:第二个安全约束下的角色配置写为
ADMINN,与实际的ADMIN角色名不匹配,会导致该规则对应的接口权限拦截失效,需修正拼写。
Keycloak Java API/Admin REST的适用场景
Keycloak Spring Boot适配器本身不会内置Keycloak服务端的操作客户端,适配器的核心作用仅为令牌校验与权限拦截。如果你需要的不是可静态注入令牌的字段,而是需要动态执行用户数据查询、属性修改、分组分配这类操作,才需要引入官方的keycloak-admin-client依赖,直接以Java API方式调用Keycloak服务,不需要手动拼接REST请求:
- 引入与当前Keycloak版本对齐的依赖(适配12.0.4版本):
<dependency> <groupId>org.keycloak</groupId> <artifactId>keycloak-admin-client</artifactId> <version>12.0.4</version> </dependency>
- 复用现有YAML中的连接配置初始化Keycloak客户端实例,无需重复配置参数:
@Bean public Keycloak keycloakClient(KeycloakProperties keycloakProperties) { return KeycloakBuilder.builder() .serverUrl(keycloakProperties.getAuthServerUrl()) .realm(keycloakProperties.getRealm()) .clientId(keycloakProperties.getResource()) .clientSecret(keycloakProperties.getCredentials().getSecret()) .grantType(OAuth2Constants.CLIENT_CREDENTIALS) .build(); }
- 直接通过实例调用对应方法即可完成用户数据查询,无需手动编写HTTP请求:
// 根据当前用户ID查询所属分组 UserResource currentUser = keycloakClient.realm("test").users().get(token.getSubject()); List<GroupRepresentation> userGroups = currentUser.groups();
这类远程调用仅适用于动态操作场景,获取常规用户档案数据无需使用该方式。
内容的提问来源于stack exchange,提问作者sushanthreddyu
相关产品推荐
相关产品推荐

