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 請求(省略錯誤處理?。?/p>
// 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);
添加回答
舉報