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

Posted

技术标签:

【中文标题】Keycloak 使用自定义协议映射器从数据库/外部源添加额外声明【英文标题】:Keycloak add extra claims from database / external source with custom protocol mapper 【发布时间】:2019-04-04 23:57:45 【问题描述】:

我已经看到这两个帖子给出了这个问题的解决方案,但他们没有为像我这样的非 Java 开发人员提供足够详细的信息:

Keycloak add extra claims from database / external source

How to register a custom ProtocolMapper in Keycloak?

这里是他们解决方案的概述,如果提供更多详细信息,可以帮助其他人。

预计流程 from 1st link

    用户登录 我的自定义协议映射器被调用,我覆盖了 transformAccessToken 方法 在这里,我将协议映射器所在的客户端作为服务登录到 keycloak。这里不要忘记使用另一个客户端 ID 而不是您正在为其构建协议映射器的那个,您将输入 否则会无限递归。 我将访问令牌获取到协议映射器中,然后调用应用程序的其余端点来获取额外的声明,即 安全。 获取端点返回的信息并将其添加为额外声明

实现它的步骤 from 2nd link

实现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>

在完成所有这些之后,调用自定义的 transformAccessToken() 方法 对 URL 的每个请求 http://:/auth/realms/testrealm/protocol/openid-connect/token

读完后我有几个问题:

    您如何“实施 ProtocolMapper” 您在哪里添加前面提到的文件? (在我的 Keycloak 安装文件夹中看不到任何 META-INF/ 目录) 如何以及在何处“添加以下接口” 自定义的 transformAccessToken() 是什么样子的

感谢大家的宝贵时间。 如果我错过了总结他们的答案,请告诉我。

编辑:

我开始赏金,希望有人能给我详细的步骤,告诉我如何在 Keycloak 3.4.3 中从数据库中添加额外的声明(对于非 Java 开发人员来说足够详细)

编辑 2 这里描述的方法可以解决问题,但缺乏细节。 Keycloak create a custom identity provider mapper

【问题讨论】:

【参考方案1】:

我希望这个分步指南对你有所帮助

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

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

    您实现了一个 CustomProtocolMapper 类,基于 AbstractOIDCProtocolMapper

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

    jboss-deployment-structure.xml 需要可用 keycloak 内置类

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

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

创建您的自定义映射器

我向您上传了我的 maven pom.xml (pom) - 只需将其导入您的 IDE,所有依赖项都会自动加载。依赖项只是 provided,稍后将在运行时直接从 keycloak 中使用

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

现在我创建了一个名为CustomOIDCProtocolMapper 的自定义协议映射器类。查找“完整”代码here

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

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

服务文件

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

fileName org.keycloak.protocol.ProtocolMapper 放在 \src\main\resources\META-INF\services\ 内的文件

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

com.***.keycloak.custom.CustomOIDCProtocolMapper

部署结构 XML

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

<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 管理 ui 中创建一个映射器并从下拉列表中选择 *** Custom Protocol Mapper

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

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

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

【讨论】:

感谢您的详细解答!从 Eclipse 我导入了 pom.xml 我添加了一个“源文件夹”,在其中我在文件夹/包 com.***.keycloak.custom 中创建了一个类 CustomOIDCProtocolMapper 我相信我到目前为止很好,但下一步需要 \src\main\资源\META-INF\services\ 文件夹。我在 Maven 项目和 Keycloak 安装文件夹中都没有找到它,我只在其中找到 bin docs domain jboss-modules.jar License.html LICENSE.txt modules @98765@4357@ themes @9876 welcome-content 您不需要在 keycloak 目录中放置任何文件(.jar 除外)。只需在eclipse中创建文件夹路径。然后将所有内容打包在 jar 中。我稍后会为您提供我的整个 Eclipse 项目 谢谢,我创建了文件夹路径,目前在将 jar 上传到 keycloak 时遇到错误 Provider com.***.keycloak.custom.CustomOIDCProtocolMapper not found 我的路径是 /com/***/keycloak/custom/CustomOIDCProtocolMapper。 java /src/main/resources/META-INF/services/org.keycloak.protocol.ProtocolMapper /src/main/resources/META-INF/jboss-deployment-结构.xml有什么问题吗? 我接受了您的回答,因为我现在可以在下拉列表中选择此自定义映射器。我将尝试了解为什么 ***CustomToken 声明不在生成的访问令牌中。谢谢大家的帮助。 我尝试按照@Evil_skunk 提供的说明进行操作,我的项目已成功部署在JBoss 上,我可以看到它在启动JBoss 时已加载。但是,当我进入 Keycloak 管理 UI 时,我看不到它。在管理 UI 中,我将进入 Clients &gt; Account &gt; Mapper &gt; Create,但在名为 Mapper Type 的选择框中找不到我的 Mapper。不确定我是否遗漏了什么?我的代码在这里:github.com/michaelisvy/keycloak-protocol-mapper/tree/master【参考方案2】:

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

==>你可以在this repository找到完整的代码。

(1) 自定义协议映射器

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

    实现AbstractOIDCProtocolMapper 及其方法setClaim(以及其他)的Java 类。 一个包含部署依赖项的jboss-deployment-structure.xml 文件。 包含自定义协议映射器全名的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):

    用户向 Keycloak 发送身份验证请求(即“登录”)。该请求是针对特定的 Keycloak 客户端发出的,例如 login-client。 由于login-client 配置为使用自定义协议映射器,因此在处理用户的身份验证请求时会执行其代码。 自定义协议映射器向 Keycloak 发送第二个身份验证请求。该请求是针对 第二个 Keycloak 客户端(例如,remote-claims-client)使用client_credentials(客户端 ID + 密钥)发出的。 自定义协议映射器接收客户端 remote-claims-client 的访问令牌。 自定义协议映射器向远程端点发送请求。一个Authorization: Bearer &lt;access token&gt; 标头被添加到请求标头中。 远程端点接收请求并验证 JWT 令牌。在许多情况下,应进一步限制访问。例如,只允许为相应的remote-claims-client 铸造(“写入”)令牌。 远程端点返回自定义远程声明数据。 自定义协议映射器接收自定义远程声明数据并将其放入用户的访问令牌中。 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 查询

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);

免责声明

我是链接存储库的所有者/作者。但是,我不是从头开始,而是使用多个其他存储库作为基础/灵感。见repo's README。

【讨论】:

以上是关于Keycloak 使用自定义协议映射器从数据库/外部源添加额外声明的主要内容,如果未能解决你的问题,请参考以下文章

keycloak 客户端协议映射器(脚本映射器)将请求标头添加到令牌中

keycloak - 使用 kcadm.sh 将协议映射器添加到 keycloak

Keycloak 中的协议映射器如何工作?

使用 kcadm.sh 在 Keycloak 中创建协议映射器

如何在 Keycloak 中创建脚本映射器?

双层设备映射器 - 自定义 dm-crypt