如何在 Keycloak 中注册自定义 ProtocolMapper?

How to register a custom ProtocolMapper in Keycloak?

我正在努力在 Keycloak 中注册自定义 ProtocolMapper。我想根据令牌请求从我的数据库中添加一些数据。所以我遵循了 .

中给出的想法

我实现了 ProtocolMapper 接口并添加了文件

META-INF/services/org.keycloak.protocol.ProtocolMapper

包含对我的 class 的引用。到目前为止一切顺利,Keycloak 识别了新的实现。我还可以通过管理控制台对其进行配置。

要向令牌添加一些数据,我想我还必须添加 one/some 接口

org.keycloak.protocol.oidc.mappers.UserInfoTokenMapper org.keycloak.protocol.oidc.mappers.OIDCIDTokenMapper org.keycloak.protocol.oidc.mappers.OIDCAccessTokenMapper

并根据接口实现方法。

现在的问题是,一旦我添加了接口,我就会收到以下日志消息:

08:55:07,292 ERROR [org.jboss.msc.service.fail] (MSC service thread 1-6) MSC000001: Failed to start service jboss.deployment.unit."keycloak-spi.jar".POST_MODULE: org.jboss.msc.service.StartException in service jboss.deployment.unit."keycloak-spi.jar".POST_MODULE
: WFLYSRV0153: Failed to process phase POST_MODULE of deployment "keycloak-spi.jar"
        at org.jboss.as.server.deployment.DeploymentUnitPhaseService.start(DeploymentUnitPhaseService.java:172)
        at org.jboss.msc.service.ServiceControllerImpl$StartTask.startService(ServiceControllerImpl.java:2032)
        at org.jboss.msc.service.ServiceControllerImpl$StartTask.run(ServiceControllerImpl.java:1955)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
        at java.lang.Thread.run(Unknown Source)
Caused by: java.lang.NoClassDefFoundError: Failed to link at/lotterien/jam/keycloak/spi/JamAuthorizationInfoProtocolMapper (Module "deployment.keycloak-spi.jar" from Service Module Loader): org/keycloak/protocol/oidc/mappers/UserInfoTokenMapper
        at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
        at sun.reflect.NativeConstructorAccessorImpl.newInstance(Unknown Source)
        at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(Unknown Source)
        at java.lang.reflect.Constructor.newInstance(Unknown Source)
        at org.jboss.modules.ModuleClassLoader.defineClass(ModuleClassLoader.java:446)
        at org.jboss.modules.ModuleClassLoader.loadClassLocal(ModuleClassLoader.java:274)
        at org.jboss.modules.ModuleClassLoader.loadClassLocal(ModuleClassLoader.java:77)
        at org.jboss.modules.Module.loadModuleClass(Module.java:713)
        at org.jboss.modules.ModuleClassLoader.findClass(ModuleClassLoader.java:190)
        at org.jboss.modules.ConcurrentClassLoader.performLoadClassUnchecked(ConcurrentClassLoader.java:412)
        at org.jboss.modules.ConcurrentClassLoader.performLoadClass(ConcurrentClassLoader.java:400)
        at org.jboss.modules.ConcurrentClassLoader.loadClass(ConcurrentClassLoader.java:116)
        at java.lang.Class.forName0(Native Method)
        at java.lang.Class.forName(Unknown Source)
        at java.util.ServiceLoader$LazyIterator.nextService(Unknown Source)
        at java.util.ServiceLoader$LazyIterator.next(Unknown Source)
        at java.util.ServiceLoader.next(Unknown Source)
        at org.keycloak.provider.DefaultProviderLoader.load(DefaultProviderLoader.java:60)
        at org.keycloak.provider.ProviderManager.load(ProviderManager.java:92)
        at org.keycloak.services.DefaultKeycloakSessionFactory.loadFactories(DefaultKeycloakSessionFactory.java:214)
        at org.keycloak.services.DefaultKeycloakSessionFactory.deploy(DefaultKeycloakSessionFactory.java:115)
        at org.keycloak.provider.ProviderManagerRegistry.deploy(ProviderManagerRegistry.java:42)
        at org.keycloak.subsystem.server.extension.KeycloakProviderDeploymentProcessor.deploy(KeycloakProviderDeploymentProcessor.java:55)
        at org.jboss.as.server.deployment.DeploymentUnitPhaseService.start(DeploymentUnitPhaseService.java:165)
        ... 5 more

为什么Keycloak找不到自己的接口?不应该开箱即用吗?

我想念什么让它发挥作用?


编辑 1

我走得更远了。我添加了一个文件

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>

现在异常消失了。

不幸的是,我的 class 中的方法 transformUserInfoToken() 仍然没有根据令牌请求调用。

谁能告诉我正确的方向?

终于成功了。即使仍有未解决的问题,我也可以实现所需的功能。

我必须实现接口

org.keycloak.protocol.oidc.mappers.OIDCAccessTokenMapper

(而不是 org.keycloak.protocol.oidc.mappers.UserInfoTokenMapper

现在,我的 transformAccessToken() 方法在每个 URL http://<host>:<port>/auth/realms/testrealm/protocol/openid-connect/token

的请求上被调用

我认为问题出在我对术语 AccessToken、UserInfo 和 IDToken 的误解。我认为这三个都是生成的令牌的一部分。但似乎 OIDCAccessTokenMapper 是将附加信息获取到访问令牌中的唯一方法。

对我来说,剩下的悬而未决的问题是 UserInfo 和 IDToken 会出现在哪里。也许有人可以给出答案。

我有针对我的具体问题的解决方案。我有同样的错误 - 但已经有一个具有可比内容的 jboss-deployment-structure.xml。它对我不起作用的原因是,我有一个包含我的实现和库作为 JAR 的 EAR。所以 jboss-deployment-structure.xml 所需的结构不同。 我 post 在这里是为了其他遇到同样问题的人:

<?xml version="1.0"?>
<jboss-deployment-structure xmlns="urn:jboss:deployment-structure:1.1">
    <ear-subdeployments-isolated>false</ear-subdeployments-isolated>
    <deployment>
        <dependencies>
            <system export="true">
                <paths>
                    <path name="com/sun/xml/internal/ws/spi"/>
                </paths>
            </system>
        </dependencies>
    </deployment>
    <sub-deployment name="org.my.implementation-1.0-SNAPSHOT.jar">
        <dependencies>
            <module name="org.keycloak.keycloak-core"/>
            <module name="org.keycloak.keycloak-server-spi"/>
            <module name="org.keycloak.keycloak-server-spi-private"/>
            <module name="org.keycloak.keycloak-services"/>
            <module name="javax.ws.rs.api"/>
        </dependencies>
    </sub-deployment>
</jboss-deployment-structure>