在 j_security_check 的响应中添加 CORS 标头

4
我正在使用jax-rs和WildFly 10构建REST API。其中一些端点是安全的。我正在使用基于FORM的身份验证。
在我的JavaScript代码中,我检查AJAX请求的响应,如果它被设置为“401未经授权”,那么我就向用户呈现一个登录表单。当他填写完毕后,我将详细信息发布到j_security_check。
当网页服务器和REST服务器在不同的机器上运行时,这一切在本地主机上都可以正常工作,但浏览器由于跨源问题拒绝了AJAX请求。
我了解CORS,因此我向我的REST服务器添加了CORS过滤器,该过滤器为GUI服务器设置CORS标头。它的所有操作都很好,除了一个小但重要的细节:登录成功后,CORS过滤器不会对j_security_check响应进行处理。没有添加CORS标头,浏览器无法读取响应。
除了这个细节外,我的整个设置都完全按照我的意愿运行……但是我一夜都在解决这个问题,而我就是无法让它正常工作。
我知道尝试过滤j_security_check存在问题,但我不知道其他添加CORS标头的方法……所以我的问题是:
如何向j_security_check的响应添加CORS标头?

你好!实现ContainerResponseFilter时,我遇到了完全相同的问题。我能够在服务器端向任何响应添加所有所需的Access-Control-Allow-XXX标头。但是对于j_security_check请求的响应是一个例外。你找到解决方案了吗?先感谢你:). - StepUp
是的...有点吧。不是很满意,但它还算能用。我会发布一个答案。 - Stijn de Witt
4个回答

4

standalone.xml/domain.xml文件中配置undertow子系统可以解决这个问题。在那里配置的过滤器将处理所有请求,包括j_security_check

<subsystem xmlns="urn:jboss:domain:undertow:3.0">
            <buffer-cache name="default"/>
            <server name="default-server">
                <http-listener name="default" redirect-socket="https" socket-binding="http"/>
                <host name="default-host" alias="localhost">
                    <location name="/" handler="welcome-content"/>
                    <filter-ref name="server-header"/>
                    <filter-ref name="x-powered-by-header"/>
                    <!--CORS headers -->
                    <filter-ref name="Access-Control-Allow-Origin"/>
                    <filter-ref name="Access-Control-Allow-Methods"/>
                    <filter-ref name="Access-Control-Allow-Headers"/>
                    <filter-ref name="Access-Control-Allow-Credentials"/>
                    <filter-ref name="Access-Control-Max-Age"/>
                </host>
            </server>
            <servlet-container name="default">
                <jsp-config/>
                <websockets/>
            </servlet-container>
            <handlers>
                <file name="welcome-content" path="${jboss.home.dir}/welcome-content"/>
            </handlers>
            <filters>
                <response-header name="server-header" header-value="WildFly/10" header-name="Server"/>
                <response-header name="x-powered-by-header" header-value="Undertow/1" header-name="X-Powered-By"/>
                <!-- CORS headers -->
                <response-header name="Access-Control-Allow-Origin" header-name="Access-Control-Allow-Origin" header-value="*"/>
                <response-header name="Access-Control-Allow-Methods" header-name="Access-Control-Allow-Methods" header-value="OPTIONS, GET, POST, PUT, DELETE"/>
                <response-header name="Access-Control-Allow-Headers" header-name="Access-Control-Allow-Headers" header-value="accept, authorization, content-type, x-requested-with"/>
                <response-header name="Access-Control-Allow-Credentials" header-name="Access-Control-Allow-Credentials" header-value="true"/>
                <response-header name="Access-Control-Max-Age" header-name="Access-Control-Max-Age" header-value="60"/>
            </filters>
        </subsystem>

当然,您最好将"*"通配符替换为您GUI服务器的URL,以代替Access-Control-Allow-Origin标头值属性中的通配符。


多么棒的解决方案 :) - StepUp
感谢您的帮助。不幸的是,多个域不允许作为cors origin值。*也不行。而且你不能在一个响应中有多个cors origin头... 这意味着如果您有多个域(大多数支持裸域url和以*www.*开头的网站),则无法使用静态配置。 - Stijn de Witt
@nfedorov 嗯,一年半过去了,在这段时间里我对这个问题进行了大量的研究,似乎你是100%正确的。你在这里描述的使用 Undertow 过滤器的方式似乎是过滤这些请求的唯一方法。Servlet 过滤器和 JAX-RS 过滤器不会被调用来处理这些请求。 - Stijn de Witt
为了解决Access-Control-Allow-Origin值必须是动态的问题,我创建了一个专门用于添加CORS头的Undertow过滤器。我会在答案中加入相关信息。 - Stijn de Witt
@nfedorov,我仍然收到“原因:CORS预检通道未成功”的消息。我需要修改什么才能使它工作? - gouessej

2

自行处理登录.

与其向 j_security_check 提交,不如提交到 /auth/login 或类似的地址并自行处理登录。可以像这样:

@POST
@Path("login")
@PermitAll
public Response postLogin() {
    String user = request.getParameter("j_username");
    String password = request.getParameter("j_password");
    StringBuffer buf = request.getRequestURL();
    URI redir = null; 
    try {redir = new URI(buf.substring(0, buf.lastIndexOf("/login")) + "/session");} 
    catch (URISyntaxException e) {}
    try {
        request.login(user, password);
        return Response.seeOther(redir).build();
    } catch (ServletException e) {
        if (e.getMessage() != null && e.getMessage().equals("UT010030: User already logged in")) {
            Response.seeOther(redir).build();
        }
        return Response.status(Status.FORBIDDEN).build();
    }
}

我不接受这个答案,因为它实际上是一个变通方法...但也许这是唯一的方法。谁知道呢?仍然希望从SO社区得到更多的意见。 - Stijn de Witt
undertow-cors-filter 目前可能是处理这个问题的最佳方式。 - Stijn de Witt

1

根据nfedorov答案,我得出结论他是正确的,只有Undertow过滤器可以操作容器响应。因此,如果我们想要向这些响应添加CORS头,则需要使用Undertow过滤器。因此,我编写了一个:

undertow-cors-filter

下载.zip文件并将其解压缩到您的Wildfly根文件夹中。然后在standalone.xml中添加filter配置:

<filters>
  <filter name="undertow-cors-filter" class-name="com.stijndewitt.undertow.cors.Filter" module="com.stijndewitt.undertow.cors">

  </filter>
</filters>

standalone.xml 文件中的 host 元素中添加一个 filter-ref
<host name="default-host" alias="localhost">
  <filter-ref name="undertow-cors-filter" />
</host>

这将添加CORS头,允许所有来源访问此服务器发出的所有响应。
向filter定义中添加参数以配置行为。例如,如果您希望该过滤器仅适用于以“/api”开头的URL请求的响应,并且只允许example.com和example.org访问,则可以使用类似于以下配置:
<filters>
  <filter name="undertow-cors-filter" class-name="com.stijndewitt.undertow.cors.Filter" module="com.stijndewitt.undertow.cors">
    <param name="urlPattern" value="^http(s)?://([^/]+)(:([^/]+))?(/([^/])+)?/api(/.*)?$" />
    <param name="policyClass" value="com.stijndewitt.undertow.cors.AllowMatching" />
    <param name="policyParam" value="^http(s)?://(www\.)?example\.(com|org)$" />   
  </filter>
</filters>

有三个策略类可用,AllowAllAllowMatchingWhitelist,如果需要可以编写自定义策略。

0

如nfedorov在早期的答案中提到的那样,在standalone.xml/domain.xml文件中配置undertow子系统可以解决这个问题。 但是,该建议的解决方案只允许在Access-Control-Allow-Origin头的值属性中指定一个域或'*'。如果API可能从多个域访问,稍微好一点的解决方案是为访问REST API的一个或多个域名设置白名单。我们可以在Undertow中实现白名单和匹配请求域与白名单而不使用任何第三方Undertow过滤器。Undertow谓词和交换属性将有助于实现此目的。可以使用正则表达式来定义要列入白名单的域列表。通过检查传入的Origin头并使用正则表达式匹配将CORS相关响应头Access-Control-Allow-Origin有条件地写入响应中以匹配白名单。所有这些都可以在Wildfly的standalone.xml中进行配置,而无需进行任何Java编码。以下文章解释了相同的内容。

https://medium.com/amritatech/a-mechanism-to-enable-cors-filter-with-no-coding-57ef2906e023


1
目前你的回答不够清晰,请编辑并添加更多细节,以帮助其他人理解它如何回答问题。你可以在帮助中心找到有关如何编写好答案的更多信息。 - Community

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接