将 Keycloak 适配器与 Wildfly 26 一起使用不提供 "KEYCLOAK" 作为机制
Using Keycloak adapter with Wildfly 26 does not provide "KEYCLOAK" as mechanism
我在 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 documentation and this thread in Google Groups 也说明了这一点。
所以我从这个位置安装了适配器和 运行 安装脚本:
./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)
- 密钥斗篷 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>
我终于在没有 Keycloak 适配器的情况下工作了,即使用新的 built-in 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>
我设法在 WildFly 26 中使用 Keycloak 和 Elytron 实现了不记名令牌授权,以便控制对企业应用程序 (.war) 的 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 服务的 war 不包含它需要的 EJB;这些在单独的 EJB 模块 (.jar) 中。我想这就是我必须在 ejb3 子系统中定义此应用程序安全域的原因。
暗流子系统
/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中的real-name是undertow子系统中定义的应用程序安全域。
问题
正如我之前所说,这个解决方案并非没有问题。鉴于我的企业应用程序 (.ear) 还有另一个 Web 模块 (.war),它包含应用程序的 GUI 组件但没有 Web 服务,只要 auth-method 该解决方案有效第二个 Web 模块是 FORM 或 BASIC。而且,您可能已经猜到了,我想使用 OIDC。
ward 使用 OIDC 控制对应用程序的访问非常简单,正如 Farah Juma in her article Securing WildFly Apps with OpenID Connect 所正确解释的那样。但只要 Keycloak 领域的“令牌声明名称”是“realm_access.roles”(其默认值),它就可以工作。使用该名称,simple-role-decoder 不起作用。所以,我想自定义 role-decoder 是必需的。鉴于我的应用程序能够自行定义和管理角色和角色分配,而不是编写自定义 role-decoder,我使用 constant-role-mapper 来获取允许 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(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,客户端得到的响应为html对应Keycloak登录页面
<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"
}
}
我希望这个部分解决方案对某人有所帮助,也希望有人能告诉我如何实施完整的解决方案。
我在 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 documentation and this thread in Google Groups 也说明了这一点。
所以我从这个位置安装了适配器和 运行 安装脚本:
./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)
- 密钥斗篷 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>
我终于在没有 Keycloak 适配器的情况下工作了,即使用新的 built-in 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>
我设法在 WildFly 26 中使用 Keycloak 和 Elytron 实现了不记名令牌授权,以便控制对企业应用程序 (.war) 的 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 服务的 war 不包含它需要的 EJB;这些在单独的 EJB 模块 (.jar) 中。我想这就是我必须在 ejb3 子系统中定义此应用程序安全域的原因。
暗流子系统
/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中的real-name是undertow子系统中定义的应用程序安全域。
问题
正如我之前所说,这个解决方案并非没有问题。鉴于我的企业应用程序 (.ear) 还有另一个 Web 模块 (.war),它包含应用程序的 GUI 组件但没有 Web 服务,只要 auth-method 该解决方案有效第二个 Web 模块是 FORM 或 BASIC。而且,您可能已经猜到了,我想使用 OIDC。
ward 使用 OIDC 控制对应用程序的访问非常简单,正如 Farah Juma in her article Securing WildFly Apps with OpenID Connect 所正确解释的那样。但只要 Keycloak 领域的“令牌声明名称”是“realm_access.roles”(其默认值),它就可以工作。使用该名称,simple-role-decoder 不起作用。所以,我想自定义 role-decoder 是必需的。鉴于我的应用程序能够自行定义和管理角色和角色分配,而不是编写自定义 role-decoder,我使用 constant-role-mapper 来获取允许 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(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,客户端得到的响应为html对应Keycloak登录页面
<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"
}
}
我希望这个部分解决方案对某人有所帮助,也希望有人能告诉我如何实施完整的解决方案。