1. 概述
Keycloak 是一个功能强大的第三方授权服务器,常用于管理 Web 或移动应用的用户身份与权限。
默认情况下,Keycloak 支持存储一些基础用户属性,例如姓名、邮箱等。但在实际项目中,这些字段往往不够用,我们通常需要添加一些业务相关的自定义用户属性,比如用户生日、会员等级、部门编号等。
本文将手把手带你实现:✅ 如何在 Keycloak 中添加自定义用户属性,并在基于 Spring 的后端服务中安全地获取这些数据。
我们会覆盖两种常见部署模式:
- ✅ 独立部署的 Keycloak 服务器(Standalone)
- ✅ 嵌入式 Keycloak(Embedded,常用于测试或轻量级场景)
踩坑提示:很多人卡在“属性明明设置了却拿不到”,根本原因通常是 claim 映射没配对,别急,后面会重点讲。
2. 独立部署的 Keycloak 服务器
2.1 添加自定义用户属性
第一步:启动 Keycloak 服务。进入 Keycloak 安装目录的 bin
文件夹,执行:
kc.bat start-dev
启动后访问管理后台:http://localhost:8180/auth/admin
登录账号密码为:initial1
/ zaq1!QAZ
进入 Manage → Users 页面,你会看到之前创建的测试用户 user1
:
点击该用户的 ID,切换到 Attributes 标签页,添加一个新属性:
- ✅ Key:
DOB
- ✅ Value:
1990-01-01
点击 Save 保存。
⚠️ 注意:此时属性只是存在数据库里,并不会自动出现在 JWT Token 中!你还得配置一个“协议映射(Protocol Mapper)”来把它暴露出去。
接下来进入你的应用客户端配置(比如之前创建的 login-app
):
点击进入客户端 → 切换到 Client Scopes → 找到并点击 login-app-dedicated
→ 进入 Mappers 标签页 → 点击 Configure a new mapper → 选择 User Attribute。
填写以下信息:
- Name: DOB
- User Attribute: DOB
- Token Claim Name: DOB
- Claim JSON Type: String
- ✅ 勾选:Add to ID token、Add to access token、Add to userinfo
保存后,这个 DOB 属性就会随着用户登录,自动注入到 JWT 的 payload 中。
2.2 获取自定义属性(Spring 后端)
现在我们来写个简单的 REST 接口,从 Token 中提取 DOB。
假设你已经集成了 Spring Security + OAuth2 Login,代码如下:
@Controller
public class CustomUserAttrController {
@GetMapping(path = "/users")
public String getUserInfo(Model model) {
final DefaultOidcUser user = (DefaultOidcUser) SecurityContextHolder.getContext()
.getAuthentication()
.getPrincipal();
String dob = "";
OidcIdToken token = user.getIdToken();
Map<String, Object> customClaims = token.getClaims();
if (customClaims.containsKey("DOB")) {
dob = String.valueOf(customClaims.get("DOB"));
}
model.addAttribute("username", user.getName());
model.addAttribute("dob", dob);
return "userInfo";
}
}
📌 核心逻辑说明:
- 通过
SecurityContextHolder
拿到当前认证主体 - 强转为
DefaultOidcUser
,从而获取IdToken
- 从
IdToken.getClaims()
中提取自定义字段DOB
配套的 Thymeleaf 模板 userInfo.html
:
<div id="container">
<h1>Hello, <span th:text="${username}">--name--</span>.</h1>
<h3>Your Date of Birth as per our records is <span th:text="${dob}"/>.</h3>
</div>
2.3 测试验证
启动 Spring Boot 应用,访问 http://localhost:8081/users
登录 user1
后,你应该能看到类似页面:
✅ 成功显示 DOB!说明整个链路打通了。
💡 小技巧:可以用 https://jwt.io 粘贴你的 ID Token 查看 payload,确认 DOB 是否真的在 Token 里。
3. 嵌入式 Keycloak 实例
如果你使用的是内嵌 Keycloak(例如通过 Docker 或内存模式运行用于测试),就不能依赖手动操作控制台了。所有配置必须预先写入 Realm 导出文件。
3.1 配置自定义属性(通过 JSON)
你需要修改 baeldung-realm.json
文件,在对应用户和客户端中声明属性与映射。
① 给用户添加 DOB 属性
找到用户 [email protected]
的定义,加入 attributes 字段:
"attributes" : {
"DOB" : "1984-07-01"
},
② 添加 Protocol Mapper 映射
在客户端的 protocolMappers
数组中加入:
"protocolMappers": [
{
"id": "c5237a00-d3ea-4e87-9caf-5146b02d1a15",
"name": "DOB",
"protocol": "openid-connect",
"protocolMapper": "oidc-usermodel-attribute-mapper",
"consentRequired": false,
"config": {
"userinfo.token.claim": "true",
"user.attribute": "DOB",
"id.token.claim": "true",
"access.token.claim": "true",
"claim.name": "DOB",
"jsonType.label": "String"
}
}
]
📌 关键配置解释:
配置项 | 说明 |
---|---|
user.attribute |
对应用户模型中的属性名 |
claim.name |
最终出现在 Token 中的字段名 |
id/access/userinfo.token.claim |
控制是否在各类 Token 中包含此 claim |
这样,当 Realm 被加载时,这些配置会自动生效。
3.2 后端获取属性(Resource Server 模式)
在资源服务器(Resource Server)中,我们通常使用 @AuthenticationPrincipal Jwt
直接读取 Token 内容。
接口代码更简洁:
@RestController
public class CustomUserAttrController {
@GetMapping("/user/info/custom")
public Map<String, Object> getUserInfo(@AuthenticationPrincipal Jwt principal) {
return Collections.singletonMap("DOB", principal.getClaimAsString("DOB"));
}
}
✅ Jwt
对象直接封装了所有 claims,调用 getClaimAsString()
即可安全取值。
3.3 编写测试用例
使用 JUnit + RestAssured 验证接口返回是否正确:
@Test
public void givenUserWithReadScope_whenGetUserInformationResource_thenSuccess() {
String accessToken = obtainAccessToken("read");
Response response = RestAssured.given()
.header(HttpHeaders.AUTHORIZATION, "Bearer " + accessToken)
.get(userInfoResourceUrl);
assertThat(response.as(Map.class)).containsEntry("DOB", "1984-07-01");
}
✅ 测试通过意味着:
- Token 中确实包含了 DOB
- 资源服务器能正确解析并返回
4. 总结
本文系统讲解了如何在 Keycloak 中添加和使用自定义用户属性,涵盖两种主流部署方式:
场景 | 关键步骤 |
---|---|
Standalone | 控制台添加属性 → 配置 Protocol Mapper → 后端解析 Token Claims |
Embedded | JSON 中预定义 attributes 和 mappers → 自动加载 → 后端通过 Jwt 取值 |
📌 核心要点回顾:
- ❌ 用户属性 ≠ Token Claim,必须通过 Protocol Mapper 显式映射
- ✅ 推荐在
ID Token
和Access Token
中都启用,避免前后端取值不一致 - ✅ 生产环境建议用脚本化配置(如 JSON 导入),避免人工误操作
- ✅ 使用
jwt.io
工具调试 Token 内容,排查问题简单粗暴
示例代码已上传 GitHub:
- 🔗 独立服务器版本:tutorials GitHub
- 🔗 嵌入式版本:OAuth GitHub
有疑问?欢迎留言讨论。掌握这套流程,以后扩展用户信息再也不是问题!