为了账号安全,请及时绑定邮箱和手机立即绑定

Keycloak 使用自定义协议映射器从数据库/外部源添加额外声明

Keycloak 使用自定义协议映射器从数据库/外部源添加额外声明

米琪卡哇伊 2021-12-22 15:14:02
我看过这两篇文章,它们为这个问题提供了解决方案,但它们没有提供足够详细的信息,说明如何为像我这样的非 Java 开发人员执行此操作:Keycloak 添加来自数据库/外部源的额外声明如何在 Keycloak 中注册自定义 ProtocolMapper?这是他们的解决方案的概述,如果提供更多详细信息,可以帮助其他人。第一个链接的预期过程用户登录我的自定义协议映射器被调用,在那里我覆盖了 transformAccessToken 方法在这里,我将协议映射器所在的客户端作为服务登录到 keycloak。这里不要忘记使用另一个客户端 ID 而不是您正在为其构建协议映射器的客户端 ID,否则您将进入无限递归。我将访问令牌放入协议映射器中,然后调用应用程序的其余端点来获取额外的声明,这是安全的。获取端点返回的信息并将其添加为额外声明从第二个链接实现它的步骤实现 ProtocolMapper 接口并添加包含对类的引用的文件 “META-INF/services/org.keycloak.protocol.ProtocolMapper”。此时,Keycloak 识别出新的实现。您应该能够通过管理控制台对其进行配置。要向令牌添加一些数据,请添加以下接口org.keycloak.protocol.oidc.mappers.OIDCAccessTokenMapper并根据接口实现方法然后添加文件“ META-INF/jboss-deployment-structure.xml ”,内容如下<?xml version="1.0" encoding="UTF-8"?><jboss-deployment-structure>    <deployment>        <dependencies>            <module name="org.keycloak.keycloak-services"/>        </dependencies>    </deployment></jboss-deployment-structure>完成所有这些之后,在对 URL http://:/auth/realms/testrealm/protocol/openid-connect/token 的每个请求时都会调用自定义的 transformAccessToken() 方法阅读本文后,我有几个问题:你如何“实施ProtocolMapper”你在哪里添加前面提到的文件?(在我的 Keycloak 安装文件夹中看不到任何 META-INF/ 目录)您如何以及在哪里“添加以下接口”自定义的 transformAccessToken() 是什么样的谢谢大家的时间。如果我错过了总结他们的答案,请告诉我。
查看完整描述

2 回答

?
慕标5832272

TA贡献1966条经验 获得超4个赞

我希望这个分步指南可以帮助你

我正在使用 Keycloak 4.5.0 - 因为我安装了这个较新的版本 - 但我不应该有太大的不同。我OIDCProtocolMapper在示例中实现了 a 。

只是总结一下 - 为了其他人的快速概览 - 每个步骤都在后面更详细地描述

  1. 您实现了一个基于 CustomProtocolMapper 的类 AbstractOIDCProtocolMapper

  2. 具有名称的 META-INF/services 文件org.keycloak.protocol.ProtocolMapper必须可用并包含映射器的名称

  3. jboss-deployment-structure.xml 需要可以使用内置于类中的keycloak

  4. Jar 文件部署在 /opt/jboss/keycloak/standalone/deployments/

好的,现在有更多细节:-)

创建您的自定义映射器

我给你上传了我的 maven pom.xmlpom ) - 只需将它导入到你的 IDE 中,所有的依赖项都会自动加载。依赖关系只是provided ,稍后将在运行时直接从 keycloak 使用

相关的是keycloak.version属性 - 当前在版本中加载所有 keycloak 依赖项4.5.0.Final

现在我创建了一个名为CustomOIDCProtocolMapper在这里找到“完整”代码

它应该扩展AbstractOIDCProtocolMapper并需要实现所有抽象方法。也许你想要一个 SAML 协议映射器然后它是另一个基类 ( AbstractSAMLProtocolMapper)

一个相关的方法是transformAccessToken- 在这里我为 AccessToken 设置了一个额外的声明。您在这里需要您的逻辑,但是是的-取决于您的数据库等;-)

服务文件

services 文件对于 keycloak 找到您的自定义实现很重要

将一个文件的文件名 org.keycloak.protocol.ProtocolMapper\src\main\resources\META-INF\services\

在此文件中,您写入自定义提供程序的名称 - 因此 keycloak 知道此类可用作协议映射器
在我的示例中,文件内容只是一行

com.stackoverflow.keycloak.custom.CustomOIDCProtocolMapper

部署结构 XML

在您的自定义映射器中,您使用来自 keycloak 的文件。为了使用它们,我们需要通知 jboss 这个依赖。因此jboss-deployment-structure.xml\src\main\resources\META-INF\ Content 中创建一个文件:

<jboss-deployment-structure>

    <deployment>

        <dependencies>

            <module name="org.keycloak.keycloak-services" />

        </dependencies>

    </deployment>

</jboss-deployment-structure>

构建和部署您的扩展

构建您的扩展 ( mvn clean package)的 jar 文件- 并将其jar放入/opt/jboss/keycloak/standalone/deployments/并重新启动 keycloak

在日志文件中,您应该看到它的部署时间和(希望没有)错误消息

现在你可以使用你的映射器 - 在我的例子中,我可以在 keycloak admin ui 中创建一个映射器并Stackoverflow Custom Protocol Mapper从下拉列表中选择

就像信息一样 - 这不是 keycloak 完全官方支持的 - 所以接口可能会在以后的版本中改变

我希望这是可以理解的,并且您将能够成功实现自己的映射器

编辑:导出的 eclipse 文件结构zip


查看完整回答
反对 回复 2021-12-22
?
拉莫斯之舞

TA贡献1820条经验 获得超10个赞

我正在使用自定义协议映射器1将经过身份验证的GraphQL 查询3发送到外部系统,并将 JSON 响应数据放入用户的访问令牌 (JWT) 中。它目前与 Keycloak 10 一起运行。

==> 你可以在这个存储库中找到完整的代码。

(1) 自定义协议映射器

正如其他人所指出的,您的项目至少需要 3 个文件。

  1. 实现AbstractOIDCProtocolMapper及其方法setClaim(以及其他方法)的Java 类。

  2. 一个jboss-deployment-structure.xml包含用于部署的依赖文件。

  3. 一个org.keycloak.protocol.ProtocolMapper包含自定义协议映射器的全名的文件。

这是文件夹结构:

$ tree src/ -A

src/

└── main

    ├── java

    │   └── com

    │       └── thohol

    │           └── keycloak

    │               └── JsonGraphQlRemoteClaim.java

    └── resources

        └── META-INF

            ├── jboss-deployment-structure.xml

            └── services

                └── org.keycloak.protocol.ProtocolMapper

(2) 认证远程请求

如果远程端点需要身份验证,我们可以从 Keycloak 获取访问令牌。完整的流程如下(尤其是步骤 3-6):

  1. 用户向 Keycloak 发送身份验证请求(即“登录”)。该请求是针对特定的 Keycloak 客户端发出的,例如login-client

  2. 由于login-client配置为使用自定义协议映射器,因此在处理用户的身份验证请求时会执行其代码。

  3. 自定义协议映射器向 Keycloak 发送第二个身份验证请求。该请求是针对第二个Keycloak 客户端(例如,remote-claims-client)使用client_credentials(客户端 ID + 秘密)进行的。

  4. 自定义协议映射器接收客户端的访问令牌remote-claims-client

  5. 自定义协议映射器向远程端点发送请求。一个Authorization: Bearer <access token>标头被添加到请求标头中。

  6. 远程端点接收请求并验证 JWT 令牌。在许多情况下,应该进一步限制访问。例如,只允许为相应的remote-claims-client.

  7. 远程端点返回自定义远程声明数据。

  8. 自定义协议映射器接收自定义远程声明数据并将其放入用户的访问令牌中。

  9. Keycloak 向用户返回带有自定义声明的访问令牌。

步骤 3/4 可以在 Java 中实现为一个简单的 HTTP POST 请求(省略错误处理!):

// Call remote service

HttpClient httpClient = HttpClient.newHttpClient();

URIBuilder uriBuilder = new URIBuilder(keycloakAuthUrl);

URI uri = uriBuilder.build();


HttpRequest.Builder builder = HttpRequest.newBuilder().uri(uri);

String queryBody = "grant_type=client_credentials&client_id=remote-claims-client&client_secret=dfebc62a-e8d7-4ab3-9196-258ddb5684ab";

builder.POST(HttpRequest.BodyPublishers.ofString(queryBody));


// Build headers

builder.header(HttpHeaders.CONTENT_TYPE , MediaType.APPLICATION_FORM_URLENCODED);


// Call

HttpResponse<String> response = httpClient.send(builder.build(), HttpResponse.BodyHandlers.ofString());


// Process Response

JsonNode json = return new ObjectMapper().readTree(response.body());

String accessToken = json.findValue("access_token").asText();

(3) 使用GraphQL Queries进行外部请求

GraphQL 查询本质上是一个 HTTP POST 请求,body类似于


{

    "query": "query HeroName($episode: Episode) {

        hero(episode: $episode) {

            name

        }

    }",

    "variables": {

        "episode" : "JEDI"

    }

}

这可以从 Java 发送,如(省略错误处理!):


HttpClient httpClient = HttpClient.newHttpClient();

URIBuilder uriBuilder = new URIBuilder(remoteUrl);

URI uri = uriBuilder.build();


HttpRequest.Builder builder = HttpRequest.newBuilder().uri(uri);

String queryBody = "{

    \"query\": \"query HeroName($episode: Episode) {

        hero(episode: $episode) {

            name

        }

    }\",

    \"variables\": {

        \"episode\" : \"JEDI\"

    }

}";

builder.POST(HttpRequest.BodyPublishers.ofString(queryBody));


// Build headers

builder.header(HttpHeaders.CONTENT_TYPE , MediaType.APPLICATION_JSON);

builder.header(HttpHeaders.AUTHORIZATION, "Bearer " + accessToken);


// Call

HttpResponse<String> response = httpClient.send(builder.build(), HttpResponse.BodyHandlers.ofString());


// Process Response and add to token

JsonNode json = return new ObjectMapper().readTree(response.body());

clientSessionCtx.setAttribute("custom_claims", json);


查看完整回答
反对 回复 2021-12-22
  • 2 回答
  • 0 关注
  • 556 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信