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

users list

点击该用户的 ID,切换到 Attributes 标签页,添加一个新属性:

  • Key: DOB
  • Value: 1990-01-01

点击 Save 保存。

⚠️ 注意:此时属性只是存在数据库里,并不会自动出现在 JWT Token 中!你还得配置一个“协议映射(Protocol Mapper)”来把它暴露出去。

接下来进入你的应用客户端配置(比如之前创建的 login-app):

clients

点击进入客户端 → 切换到 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 tokenAdd to access tokenAdd to userinfo

new mapper

保存后,这个 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

✅ 成功显示 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 TokenAccess Token 中都启用,避免前后端取值不一致
  • ✅ 生产环境建议用脚本化配置(如 JSON 导入),避免人工误操作
  • ✅ 使用 jwt.io 工具调试 Token 内容,排查问题简单粗暴

示例代码已上传 GitHub:

有疑问?欢迎留言讨论。掌握这套流程,以后扩展用户信息再也不是问题!


原始标题:Custom User Attributes with Keycloak | Baeldung