【问题标题】:Using Keycloak adapter with Wildfly 26 does not provide "KEYCLOAK" as mechanism将 Keycloak 适配器与 Wildfly 26 一起使用不会提供“KEYCLOAK”作为机制
【发布时间】:2022-01-31 07:46:08
【问题描述】:

我在 WildFly 中部署了一个 JAX-RS 应用程序。应用程序的端点应由带有Access Type: bearer-only 的 Keycloak 保护。这对于高达 24 的 WildFly 版本非常有效。

从 WildFly 25 开始,Keycloak 适配器已弃用,应迁移到新的 Elytron 子系统。根据此 WildFly 问题https://issues.redhat.com/browse/WFLY-15485,但是 OIDC 适配器尚未准备好与bearer-only 一起使用。但有人提到,使用 Keycloak Wildfly 适配器应该仍然可以。

latest Keycloak documentationthis thread in Google Groups 也说明了这一点。

所以我从这个位置安装了适配器并运行了安装脚本:

https://github.com/keycloak/keycloak/releases/download/16.1.1/keycloak-oidc-wildfly-adapter-16.1.1.zip

./bin/jboss-cli.sh --file=bin/adapter-elytron-install-offline.cli -Dserver.config=standalone-full.xml

部署应用程序时,我收到以下错误消息:

java.lang.IllegalStateException: The required mechanism 'KEYCLOAK' is not available in mechanisms [BASIC, CLIENT_CERT, DIGEST, FORM] from the HttpAuthenticationFactory

设置

  • WildFly 26(雅加达 EE 8)
  • Keycloak 16.1.1

web.xml

<web-app xmlns="http://java.sun.com/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
         version="3.0">

    <!-- Security configuration -->
    <security-constraint>
        <web-resource-collection>
            <web-resource-name>admin-api</web-resource-name>
            <url-pattern>/administration/*</url-pattern>
            <url-pattern>/operations/*</url-pattern>
            <url-pattern>/applications/*</url-pattern>
            <url-pattern>/entities/*</url-pattern>
        </web-resource-collection>
    </security-constraint>

    <login-config>
        <auth-method>KEYCLOAK</auth-method>
        <realm-name>this is ignored currently</realm-name>
    </login-config>

    <security-role>
        <role-name>*</role-name>
    </security-role>

</web-app>

【问题讨论】:

    标签: wildfly keycloak wildfly-26


    【解决方案1】:

    我设法在 WildFly 26 中使用 Keycloak 和 Elytron 实现承载令牌授权,以控制对企业应用程序 (.ear) 的 Web 模块 (.war) 中的 RESTful Web 服务的访问,但解决方案并非没有问题.这就是我所做的:

    定义一个 elytron 令牌领域

    /subsystem=elytron/token-realm=xyz2ap112-token-realm/:add(\
        principal-claim=preferred_username,\
        oauth2-introspection={\
            client-id=xyz2ap112-web-api,\
            client-secret=${env.keycloak_client_secret},\
            introspection-url=${env.keycloak_introspection_url}\
        }\
    )
    

    定义一个 elytron 角色解码器

    /subsystem=elytron/simple-role-decoder=xyz2ap112-realm-access-roles/:add(\
        attribute=realm_access_roles\
    )
    

    警告:Keycloak 领域的默认“令牌声明名称”是“realm_access.roles”。为了让这个角色解码器工作,我不得不把它改成“realm_access_roles”(没有点)。当我谈到这个解决方案的问题时,我会再次提到这一点。

    定义 elytron 安全域

    /subsystem=elytron/security-domain=xyz2ap112-token-security-domain/:add(\
        realms=[{realm="xyz2ap112-token-realm",role-decoder="xyz2ap112-realm-access-roles"}],\
        default-realm=xyz2ap112-token-realm,\
        permission-mapper=default-permission-mapper\
    )
    

    定义一个 elytron HTTP 认证工厂

    /subsystem=elytron/http-authentication-factory=xyz2ap112-web-api-authentication-factory/:add(\
        security-domain=xyz2ap112-token-security-domain,\
        mechanism-configurations=[{\
            mechanism-name=BEARER_TOKEN,\
            mechanism-realm-configurations=[realm-name=xyz2ap112-token-realm]\
        }],\
        http-server-mechanism-factory=global\
    )
    

    定义两个应用程序安全域

    ejb3 子系统

    /subsystem=ejb3/application-security-domain=xyz2ap112-web-api-security-domain/:add(\
        security-domain=xyz2ap112-token-security-domain\
    )
    

    警告:包含 Web 服务的战争不包含它需要的 EJB;它们位于单独的 EJB 模块 (.jar) 中。我想这就是我必须在 ejb3 子系统中定义这个应用程序安全域的原因。

    undertow 子系统

    /subsystem=undertow/application-security-domain=xyz2ap112-web-api-security-domain/:add(\
        http-authentication-factory=xyz2ap112-web-api-authentication-factory,\
        override-deployment-config=true\
    )
    

    配置应用的 jboss-web.xml 和 web.xml

    <jboss-web>
        <context-root>/xyz2ap112-web-api</context-root>
        <resource-ref>
            <res-ref-name>jdbc/xyz2ap112</res-ref-name> <!-- Logical name only. -->
            <jndi-name>java:/jdbc/xyz2ap112</jndi-name> <!-- Real JNDI name. -->
        </resource-ref>
        <security-domain>xyz2ap112-web-api-security-domain</security-domain>
    </jboss-web>
    

    security-domain是undertow子系统中定义的应用安全域。

    <login-config>
        <auth-method>BASIC</auth-method>
        <realm-name>xyz2ap112-web-api-security-domain</realm-name>
    </login-config>
    

    login-config中的实名是undertow子系统中定义的应用安全域。

    问题

    正如我之前所说,这个解决方案并非没有问题。鉴于我的企业应用程序 (.ear) 还有另一个 Web 模块 (.war),其中包含应用程序的 GUI 组件且没有 Web 服务,只要该第二个 Web 模块的 auth-method 是 FORM 或基本的。而且,您可能已经猜到了,我想使用 OIDC。

    使用 OIDC 控制对应用程序的访问非常简单,正如 Farah Juma 在她的文章 Securing WildFly Apps with OpenID Connect 中正确解释的那样。但只要 Keycloak 领域的“令牌声明名称”是“realm_access.roles”(其默认值),它就可以工作。使用该名称,简单角色解码器不起作用。所以,我想需要一个自定义角色解码器。鉴于我的应用程序能够自行定义和管理角色和角色分配,而不是编写自定义角色解码器,我使用常量角色映射器来获取允许 Web 服务执行和检查权限的单个角色使用应用程序中定义的角色。再一次,只要第二个 Web 模块的 auth-method 是 FORM 或 BASIC,它就可以工作;使用 OIDC,不执行 Web 服务;客户端获得 HTTP 500(见下文)。任何正在运行的 WildFly(Keycloak 和应用程序)的日志中都没有其他信息。

    这是 GUI Web 模块的 oidc.json 文件:

    {
        "client-id": "xyz2ap112-web",
        "confidential-port": 8543,
        "principal-attribute": "preferred_username",
        "provider-url": "http://localhost:8180/auth/realms/jrcam",
        "public-client": true,
        "ssl-required": "external"
    }
    

    这是客户端异常:

    Exception in thread "main" javax.ws.rs.InternalServerErrorException: HTTP 500 Internal Server Error
        at org.glassfish.jersey.client.JerseyInvocation.convertToException(JerseyInvocation.java:1098)
        at org.glassfish.jersey.client.JerseyInvocation.translate(JerseyInvocation.java:883)
        at org.glassfish.jersey.client.JerseyInvocation.lambda$invoke$1(JerseyInvocation.java:767)
        at org.glassfish.jersey.internal.Errors.process(Errors.java:316)
        at org.glassfish.jersey.internal.Errors.process(Errors.java:298)
        at org.glassfish.jersey.internal.Errors.process(Errors.java:229)
        at org.glassfish.jersey.process.internal.RequestScope.runInScope(RequestScope.java:414)
        at org.glassfish.jersey.client.JerseyInvocation.invoke(JerseyInvocation.java:765)
        at org.glassfish.jersey.client.JerseyInvocation$Builder.method(JerseyInvocation.java:428)
        at org.glassfish.jersey.client.JerseyInvocation$Builder.get(JerseyInvocation.java:324)
        at org.xyz.jax.rs.client.base.AbstractFacadeServiceClient.find(AbstractFacadeServiceClient.java:28)
        at xyz2.BarrioFacadeClient.find(BarrioFacadeClient.java:40)
        at xyz2.BarrioFacadeClient.main(BarrioFacadeClient.java:24)
    

    如果Web Services Web模块的auth-method是OIDC,则客户端得到的响应是Keycloak登录页面对应的html。

    <html xmlns="http://www.w3.org/1999/xhtml" class="login-pf">
        ...
                        <h1 id="kc-page-title">
                            Sign in to your account
                        </h1>
        ...
    </html>
    

    这是 Web 服务 Web 模块的 oidc.json 文件:

    {
        "client-id": "xyz2ap112-web-api",
        "confidential-port": 8543,
        "principal-attribute": "preferred_username",
        "provider-url": "http://localhost:8180/auth/realms/jrcam",
        "ssl-required": "external",
        "bearer-only": true,
        "verify-token-audience": true,
        "realm-public-key": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAk3PD30r3SQBqnO15g/Jc5z3NFnt9HLA6QlQt2QLtxvGhLcerTD2rVWCst/4NSQev9dBscFnwxXyAoZAqTm7w0oPzlhw1Xbqt1dpKdNjMtbJxmpqzCRLTjmNatPmoAGx+9TWOPKw1qfEwZOy9xOqnCbBeT5eGCAXci+wvt8mpNX9lpAguFxgpFtyVc0at35Lw3BdZ13+6Ljxu6Z+mam1tQ9mwey0ubfhV3NK0eN8jruKWrCyGw6DRbmvKFTwQa5akDbMWt3H/HaSLMXBOrBKq9He6azVL3dkbdd40drgHtI8G+ANC1NhOPzjPtuifo9U2wHD6o8S03o35mm4xjJNcqQIDAQAB",
        "credentials": {
            "secret": "8c98045a-4640-46e7-9f68-74a289e43b7e"
        }
    }
    

    我希望这个部分解决方案对某人有所帮助,也希望有人能告诉我如何实施一个完整的解决方案。

    【讨论】:

      【解决方案2】:

      我终于让它在没有 Keycloak 适配器的情况下工作,即使用新的内置 Elytron 子系统。

      oidc.json(位于WEB-INF目录中)

      {
        "realm": "myrealm",
        "client-id": "my-client-app",
        "auth-server-url": "${keycloak.url}/auth",
        "provider-url": "${keycloak.url}/auth/realms/myrealm",
        "bearer-only": true,
        "enable-cors": true,
        "ssl-required": "none"
      }
      

      web.xml

      <web-app xmlns="http://java.sun.com/xml/ns/javaee"
               xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
               xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
               version="3.0">
      
          <!-- other configuration -->
      
          <login-config>
              <auth-method>OIDC</auth-method>
          </login-config>
      
      </web-app>
      

      【讨论】:

      • 嗨罗伯特!我尝试了这个解决方案,但它对我不起作用。 Web 服务的客户端得到的响应是 Keycloak 登录页面的 HTML。类似的事情发生在你身上吗?如果有,你是怎么解决的?
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-02-05
      • 2022-11-05
      • 1970-01-01
      • 2020-06-05
      • 1970-01-01
      • 2023-01-20
      相关资源
      最近更新 更多