如何在 Keycloak 身份验证之前调用 javax.servlet.Filter

Posted

技术标签:

【中文标题】如何在 Keycloak 身份验证之前调用 javax.servlet.Filter【英文标题】:How to get javax.servlet.Filter called before Keycloak Authentication 【发布时间】:2018-12-25 12:43:51 【问题描述】:

我们使用 resteasy 开发了一个 REST API。 (部署在wildfly 10中)

基本上,这些 REST API 是从另一个应用程序内部调用的,并且端点由 keycloak 保护。

但是一个端点暴露给外部方(该端点也被 keycloak 保护)。

但是由于外部方无法提供 Keycloak 授权代码,我们已经完成了一个实现,其中客户端使用应用程序生成的 auth_key 注册,客户端将使用该 auth_key 调用端点。

然后在 web 过滤器 (a javax.servlet.Filter) 中,使用 tha auth_key 我们获得相关的 keycloak authntication Bearer 令牌。如果需要(例如:令牌过期),我们也会调用 Keycloak 服务器。收到后,我们将该授权令牌添加到 Web 过滤器中的 httpRequest 并继续执行端点应用程序。

但问题是,KeyCloak 身份验证是在 Web Filter 之前调用的。 我正在寻找的是“如何在 keycloak 身份验证之前调用 Web 过滤器?”

编辑:

现在我正在尝试找到这里提到的方法。 Setting Request Header to Request Before Authentication Happens in Keycloak。在那里我可以在身份验证发生之前接到电话。 但我无法在那里设置请求标头。

web.xml

<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns="http://java.sun.com/xml/ns/javaee"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
    version="3.0">
    <display-name>Restful Web Application</display-name>
    <context-param>
        <param-name>resteasy.scan</param-name>
        <param-value>true</param-value>
    </context-param>

    <!-- keycloak -->

    <context-param>
        <param-name>keycloak.config.resolver</param-name>
        <param-value>package.to.HeaderBasedKeycloakConfigResolver</param-value>
    </context-param>

    <security-constraint>
        <web-resource-collection>
            <web-resource-name>REST endpoints</web-resource-name>
            <url-pattern>/ep-name/resource-name</url-pattern>
        </web-resource-collection>
        <auth-constraint>
            <role-name>resource-name</role-name>
        </auth-constraint>
    </security-constraint>

    <!-- more security-constraint -->
    <!-- more security-constraint -->
    <!-- more security-constraint -->

    <login-config>
        <auth-method>KEYCLOAK</auth-method>
        <realm-name>realm-name</realm-name>
    </login-config>

    <security-role>
        <role-name>role-name-for-resource-1</role-name>
        <role-name>role-name-for-resource-2</role-name>
        <!-- more security-role -->
        <!-- more security-role -->
        <!-- more security-role -->
    </security-role>

    <listener>
        <listener-class>
            org.jboss.resteasy.plugins.server.servlet.ResteasyBootstrap</listener-class>
    </listener>

    <servlet>
        <servlet-name>resteasy-servlet</servlet-name>
        <servlet-class>
            org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher
        </servlet-class>
        <init-param>
            <param-name>resteasy.servlet.mapping.prefix</param-name>
            <param-value>/ep-name</param-value>
        </init-param>
    </servlet>

    <servlet-mapping>
        <servlet-name>resteasy-servlet</servlet-name>
        <url-pattern>/ep-name/*</url-pattern>
    </servlet-mapping>

    <filter>
      <filter-name>WebFilter</filter-name>
      <filter-class>package.to.filter.WebFilter</filter-class>
   </filter>

   <filter-mapping>
      <filter-name>WebFilter</filter-name>
      <url-pattern>/desired-ep-name/*</url-pattern>
   </filter-mapping>

</web-app>

【问题讨论】:

您找到解决问题的方法了吗? 我还是没有找到回复。我以另一种方式管理它。但现在我正试图找到一种方法,如此处所述。 ***.com/questions/52551723/… 我可以在身份验证发生之前接到电话。但我无法设置请求标头。 对于一个不错的解决方案,请参阅arjan-tijms.omnifaces.org/2012/12/… 仅适用于 undertow,但它很好地说明了这个想法 【参考方案1】:

您是否尝试过更改 web.xml 中元素的顺序(例如,将过滤器定义放在 BEFORE servlet 定义中)?

不确定它是否有效,但文档说: "过滤器在链中的顺序与过滤器映射出现在 Web 应用程序部署描述符中的顺序相同"

servlet 和过滤器之间的顺序也可能是这样的原理......

【讨论】:

感谢@TacheDeChoco 的回答。但它不起作用。 接受,因为它很有帮助,虽然不是我的问题的正确解决方案 @namalfernandolk 您找到解决问题的方法了吗?在上面的 cmets 中,您提到此解决方案不起作用 @tryingToLearn :是的,我仍然没有找到回复。我用另一种方式管理它。但现在我正试图找到一种方法,如此处所述。 ***.com/questions/52551723/…。在那里我可以在身份验证发生之前接到电话。但我无法设置请求标头。【参考方案2】:

这可能是因为 Tomcat Authentication Valve 在过滤器之前启动。 检查 Authenticators.properties 文件。您可能需要将过滤器类包装为 Valve

【讨论】:

感谢 Adrain0 的回答。我检查了这个。 @namalfernandolk 你找到解决方案了吗? 还是不行!但我采用了另一种方法。我发现我肯定会在这里更新,这样可以挽救他人的生命 @namalfernandolk 你采用的另一种方法是什么? @tryingToLearn : 它基于 Riderection 和另一个 api。【参考方案3】:

我知道这已经 2 岁了,但我找到了解决此问题的方法:将您的适配器更改为 servlet 过滤器,我的是 jboss/wildfly,然后添加到您的过滤器下方的 web.xml 以更改请求或响应。在 jboss/wildfly 的情况下,身份验证发生在一个阀门中,因此身份验证发生在任何过滤器执行之前。完成此操作后,您可以控制 keycloak 何时工作并将您的过滤器放在它之前。

web.xml

<filter>
        <filter-name>Keycloak Filter</filter-name>
        <filter-class>org.keycloak.adapters.servlet.KeycloakOIDCFilter</filter-class>
</filter>
    <filter-mapping>
        <filter-name>Keycloak Filter</filter-name>
        <url-pattern>/restricted/*</url-pattern>
        <url-pattern>/restricted/</url-pattern>
</filter-mapping>

将适当版本的keycloak-servlet-filter-adapter 添加到您的 pom 中,并从 web.xml 中删除所有其他安全约束、身份验证方法等。

【讨论】:

以上是关于如何在 Keycloak 身份验证之前调用 javax.servlet.Filter的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Spring Security 中将令牌转换为身份验证?

尝试调用 AIA 时,Keycloak“处理对身份提供者的身份验证请求时出现意外错误”

使用有效的 JWT 令牌调用经过 keycloak 身份验证的 API

与 Keycloak 身份验证插件集成时,无法从 rest 客户端调用 nuxeo rest api

“限制身份验证会话”如何在 keycloak 中工作

如何在使用 keycloak 进行身份验证时处理 uri 重定向