Keycloak 自定义用户属性

1、概览

Keycloak 是第三方授权服务器,负责管理我们 Web 或移动应用的用户。

它提供了一些默认属性,例如名字、姓氏和电子邮件,用于存储任何给定用户的信息。但是很多时候,这些属性是不够的,我们可能需要添加一些特定于我们应用的额外用户属性。

本文将带你了解如何在 Keycloak 授权服务器中添加自定义用户属性,并在基于 Spring 的后端中访问它们。

2、独立服务器

2.1、添加自定义用户属性

基于上文 “Spring Boot 整合 Keycloak” 中的 Keycloak 服务器。

第一步是进入 Keycloak 的管理控制台。

在 Keycloak 发行版的 bin 文件夹中运行如下命令来启动服务器:

kc.bat start-dev

然后,进入 Web 管理控制台 http://localhost:8180/auth/admin,输入凭证:initial1 / zaq1!QAZ

接下来,点击 “Manage” 选项卡下的 “Users”:

用户列表

在这里,可以看到 之前(上一篇文章)添加的用户:user1

点击 “ID”,然后转到 “Attributes” 选项卡,添加一个新的属性,即表示出生日期的 “DOB”:

user1

点击 “Save” 后,自定义属性就会添加到用户信息中。

接下来,将此属性作为自定义 Claim 添加到映射中,以便它在用户 Token 的 JSON Payload 中可用。

为此,需要进入管理控制台的 “应用客户端”。回想一下 之前 创建的客户端 login-app

客户端

点击它,然后选择 “Client scopes” 选项卡。在 “Client scopes” 页面点击 “login-app-dedicated” 链接,进入 “Mappers” 选项卡。点击 “Configure a new mapper”,选择 “User Attribute” 创建新 mapper:

创建新 Mapper

NameUser AttributeToken Claim 名称设为 DOBClaim JSON Type 设置为 String

点击 “Save” 后,映射就完成了。现在,可以从 Keycloak 端接收 DOB 作为自定义用户属性了。

2.2、访问自定义用户属性

上文 Spring Boot 应用 的基础上,添加一个新的 REST Controller 来获取添加的用户属性:

@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";
    }

}

如上,首先从 Security Context 中获取 Authentication,并从中提取了 OidcUser。然后,获得了 IDToken。最后就可以从 IDTokenClaims 中提取 DOB

下面是显示这些信息的模板,名为 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、测试

启动 Boot 应用后,访问 http://localhost:8081/users。首先会要求我们输入凭证。

输入 user1 的凭证后,应该会看到如下页面:

DOB

3、嵌入式服务器

现在来看看如何在嵌入式 Keycloak 实例上实现同样的功能。

3.1、添加自定义用户属性

基本上,这里的执行步骤相同。只是需要将它们作为预配置保存到 Realm 定义文件 baeldung-realm.json 中。

要在用户 john@test.com 中添加 DOB 属性,首先需要配置其属性:

"attributes" : {
    "DOB" : "1984-07-01"
},

然后为 DOB 添加 Protocol Mapper:

"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"
        }
    }
]

这就行了。

在知道了如何向授权服务器添加自定义用户属性后,来看一下资源服务器如何访问用户的出生日期(DOB)。

3.2、访问自定义用户属性

在资源服务器端,自定义属性将作为 AuthenticationPrincipal 中的 Claim(声明)值提供给我们。

定义一个 API:

@RestController
public class CustomUserAttrController {
    @GetMapping("/user/info/custom")
    public Map<String, Object> getUserInfo(@AuthenticationPrincipal Jwt principal) {
        return Collections.singletonMap("DOB", principal.getClaimAsString("DOB"));
    }
}

3.3、测试

使用 JUnit 进行测试。首先需要获取 Access Token,然后调用资源服务器上的 /user/info/custom API 端点:

@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");
}

如上,在测试方法中验证了所获得的 DOB 值与在用户属性中添加的相同。

4、总结

本文介绍了如何在独立 Keycloak 服务器和嵌入式 Keycloak 服务器中为用户添加额外属性,以及如何在资源服务器中访问用户的自定义属性。


Ref:https://www.baeldung.com/keycloak-custom-user-attributes