将 CORS headers 添加到 j_security_check 的响应
Add CORS headers to response of j_security_check
我正在使用 jax-rs 和 WildFly 10 构建 REST API。一些端点是安全的。我正在使用基于 FORM 的身份验证。
在我的 javascript 代码中,我检查了 AJAX
请求的响应,如果它被设置为 401 Unauthorized
,我就会向用户显示一个登录表单。当他填写的时候,我POST详细到j_security_check
。
运行 在 localhost
上一切正常,但是当网络服务器和 REST 服务器在不同的机器上时,浏览器拒绝 AJAX 请求,原因是 cross-origin 问题。
我理解 CORS,所以我在我的 REST 服务器中添加了一个 CORS 过滤器,它为 GUI 服务器设置了 CORS headers。一切正常,除了一个小但重要的细节:登录成功后,CORS 过滤器不会针对 j_security_check
响应触发。没有添加 CORS headers,浏览器无法读取响应。
除了这个细节之外,我的整个设置都完全按照我想要的方式工作....但是我整晚都在为这个细节而苦苦挣扎,我就是无法让它工作。
我知道尝试过滤 j_security_check
存在问题,但我知道没有其他方法可以添加 CORS headers...所以我的问题是:
如何将 CORS headers 添加到 j_security_check
的响应中?
自行处理登录。
而不是 post 到 j_security_check
,post 到 /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();
}
}
在 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>
当然,您最好在 Access-Control-Allow-Origin
header 的值属性中用您的 GUI 服务器的 url 替换 "*"
通配符。
基于 nfedorov's answer,我得出的结论是他是对的,Undertow 过滤器是唯一可以对容器响应进行操作的过滤器类型。因此,如果我们想将 CORS headers 添加到这些响应中,我们需要一个 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>
在host
元素中添加一个filter-ref
(仍在standalone.xml
中):
<host name="default-host" alias="localhost">
<filter-ref name="undertow-cors-filter" />
</host>
这将添加 CORS headers 允许所有来源访问此服务器发出的所有响应。
将 param
添加到 filter
定义以配置行为。例如,如果您希望过滤器仅应用于对以 /api
开头的 URLS 请求的响应,并且您只想允许 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>
有 3 个策略 类 可用,AllowAll
、AllowMatching
和 Whitelist
,如果需要,您可以编写自己的自定义策略。
正如 nfedorov 之前的回答中提到的,在 standalone.xml/domain.xml 文件中配置 undertow 子系统可以解决这个问题。
但该建议的解决方案只允许在 Access-Control-Allow-Origin header 的值属性中指定一个域或“*”。稍微好一点的解决方案是将访问 REST API 的一个或多个域列入白名单,如果可能从多个域访问这些 API。我们可以在不使用任何第 3 方 Undertow 过滤器的情况下实现白名单并将请求域与 Undertow 中的白名单进行匹配。 Undertow 谓词和交换属性将有助于实现这一目标。正则表达式可用于定义要列入白名单的域列表。 CORS 相关响应 header Access-Control-Allow-Origin 可以有条件地写入响应,方法是检查传入的 Origin header 并使用正则表达式匹配将其与白名单进行匹配。所有这些都可以在 Wildfly 的 standalone.xml 中配置,无需任何 Java 编码。以下文章解释相同。
https://medium.com/amritatech/a-mechanism-to-enable-cors-filter-with-no-coding-57ef2906e023
我正在使用 jax-rs 和 WildFly 10 构建 REST API。一些端点是安全的。我正在使用基于 FORM 的身份验证。
在我的 javascript 代码中,我检查了 AJAX
请求的响应,如果它被设置为 401 Unauthorized
,我就会向用户显示一个登录表单。当他填写的时候,我POST详细到j_security_check
。
运行 在 localhost
上一切正常,但是当网络服务器和 REST 服务器在不同的机器上时,浏览器拒绝 AJAX 请求,原因是 cross-origin 问题。
我理解 CORS,所以我在我的 REST 服务器中添加了一个 CORS 过滤器,它为 GUI 服务器设置了 CORS headers。一切正常,除了一个小但重要的细节:登录成功后,CORS 过滤器不会针对 j_security_check
响应触发。没有添加 CORS headers,浏览器无法读取响应。
除了这个细节之外,我的整个设置都完全按照我想要的方式工作....但是我整晚都在为这个细节而苦苦挣扎,我就是无法让它工作。
我知道尝试过滤 j_security_check
存在问题,但我知道没有其他方法可以添加 CORS headers...所以我的问题是:
如何将 CORS headers 添加到 j_security_check
的响应中?
自行处理登录。
而不是 post 到 j_security_check
,post 到 /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();
}
}
在 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>
当然,您最好在 Access-Control-Allow-Origin
header 的值属性中用您的 GUI 服务器的 url 替换 "*"
通配符。
基于 nfedorov's answer,我得出的结论是他是对的,Undertow 过滤器是唯一可以对容器响应进行操作的过滤器类型。因此,如果我们想将 CORS headers 添加到这些响应中,我们需要一个 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>
在host
元素中添加一个filter-ref
(仍在standalone.xml
中):
<host name="default-host" alias="localhost">
<filter-ref name="undertow-cors-filter" />
</host>
这将添加 CORS headers 允许所有来源访问此服务器发出的所有响应。
将 param
添加到 filter
定义以配置行为。例如,如果您希望过滤器仅应用于对以 /api
开头的 URLS 请求的响应,并且您只想允许 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>
有 3 个策略 类 可用,AllowAll
、AllowMatching
和 Whitelist
,如果需要,您可以编写自己的自定义策略。
正如 nfedorov 之前的回答中提到的,在 standalone.xml/domain.xml 文件中配置 undertow 子系统可以解决这个问题。 但该建议的解决方案只允许在 Access-Control-Allow-Origin header 的值属性中指定一个域或“*”。稍微好一点的解决方案是将访问 REST API 的一个或多个域列入白名单,如果可能从多个域访问这些 API。我们可以在不使用任何第 3 方 Undertow 过滤器的情况下实现白名单并将请求域与 Undertow 中的白名单进行匹配。 Undertow 谓词和交换属性将有助于实现这一目标。正则表达式可用于定义要列入白名单的域列表。 CORS 相关响应 header Access-Control-Allow-Origin 可以有条件地写入响应,方法是检查传入的 Origin header 并使用正则表达式匹配将其与白名单进行匹配。所有这些都可以在 Wildfly 的 standalone.xml 中配置,无需任何 Java 编码。以下文章解释相同。
https://medium.com/amritatech/a-mechanism-to-enable-cors-filter-with-no-coding-57ef2906e023