2 回答
TA贡献1966条经验 获得超4个赞
我希望这个分步指南可以帮助你
我正在使用 Keycloak 4.5.0 - 因为我安装了这个较新的版本 - 但我不应该有太大的不同。我OIDCProtocolMapper
在示例中实现了 a 。
只是总结一下 - 为了其他人的快速概览 - 每个步骤都在后面更详细地描述
您实现了一个基于 CustomProtocolMapper 的类
AbstractOIDCProtocolMapper
具有名称的 META-INF/services 文件
org.keycloak.protocol.ProtocolMapper
必须可用并包含映射器的名称jboss-deployment-structure.xml
需要可以使用内置于类中的keycloakJar 文件部署在
/opt/jboss/keycloak/standalone/deployments/
好的,现在有更多细节:-)
创建您的自定义映射器
我给你上传了我的 maven pom.xml
( pom ) - 只需将它导入到你的 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
TA贡献1820条经验 获得超10个赞
我正在使用自定义协议映射器1将经过身份验证的2 GraphQL 查询3发送到外部系统,并将 JSON 响应数据放入用户的访问令牌 (JWT) 中。它目前与 Keycloak 10 一起运行。
==> 你可以在这个存储库中找到完整的代码。
(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 <access token>
标头被添加到请求标头中。远程端点接收请求并验证 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 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);
添加回答
举报