自定义 HTTP Header 块 Jersey CORS 过滤器
Custom HTTP Header blocks Jersey CORS Filter
我正在使用 Jersey2 响应过滤器来处理来自浏览器的 CORS 请求。在这一点上,它看起来很像 Paul Samsotha 在这个问题中的那个
How to handle CORS using JAX-RS with Jersey
@Provider
public class CORSFilter implements ContainerRequestFilter, ContainerResponseFilter {
@Override
public void filter(ContainerRequestContext containerRequestContext, ContainerResponseContext containerResponseContext)
throws IOException {
if (containerRequestContext.getHeaderString("Origin") == null) {
return;
}
if (isPreflightRequest(containerRequestContext)) {
containerResponseContext.getHeaders()
.add("Access-Control-Allow-Credentials", "true");
containerResponseContext.getHeaders()
.add("Access-Control-Allow-Headers", "origin, content-type, accept, authorization");
containerResponseContext.getHeaders()
.add("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS, HEAD");
}
containerResponseContext.getHeaders()
.add("Access-Control-Allow-Origin", "*");
}
@Override
public void filter(ContainerRequestContext containerRequestContext) throws IOException {
if (isPreflightRequest(containerRequestContext)) {
containerRequestContext.abortWith(Response.ok().build());
return;
}
}
private static boolean isPreflightRequest(ContainerRequestContext request) {
return request.getHeaderString("Origin") != null
&& request.getMethod().equalsIgnoreCase("OPTIONS");
}
}
现在一切正常,直到我像这样在前端添加自己的 header(用于授权目的)
axios.defaults.headers.common["xy"] = "example content";
在服务器端我有一个简单的DELETE方法作为例子
@Path("/example")
@DELETE
public void example(@Context HttpHeaders headers) {
String headerContent = headers.getHeaderString("xy");
...do stuff...
}
添加此 header 后,将不再为主调用执行过滤器。它仍然为预检执行,但不再为 DELETE 调用执行,这会在浏览器中触发 CORS-Error。
从 Postman 对其进行测试完全相同 header。我发出呼叫,过滤器触发,一切正常。但是浏览器一运行,过滤器就不再触发了。
我也对 PUT 进行了测试,结果相同。我用 vanilla JS XMLHTTPRequest 测试了它,结果相同。通过远程调试我知道过滤器根本没有被执行。当我删除 header 时,我仍然有 OPTIONS 和 DELETE 调用,但是对这两个调用都执行了过滤器。当我再次添加 header 时,它只针对 OPTIONS 调用(没有 header)执行,但不会针对 DELETE 调用执行。
这在 Tomcat 9 上运行,Jersey 2.27 和 Java 11。
所以我的问题是,为什么 header 似乎会终止过滤器的执行?
您似乎添加了多个条件来应用过滤器。
尝试禁用以下条件:(containerRequestContext.getHeaderString("Origin") == null)
和 isPreflightRequest(containerRequestContext)
。
例如:
@Override
public void filter(ContainerRequestContext containerRequestContext, ContainerResponseContext containerResponseContext)
throws IOException {
// disable this condition.
//if (containerRequestContext.getHeaderString("Origin") == null) {
// return;
//}
// disable this condition
// it seems problemetic
// if (isPreflightRequest(containerRequestContext)) {
containerResponseContext.getHeaders()
.add("Access-Control-Allow-Credentials", "true");
containerResponseContext.getHeaders()
.add("Access-Control-Allow-Headers", "origin, content-type, accept, authorization");
containerResponseContext.getHeaders()
.add("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS, HEAD");
//}
containerResponseContext.getHeaders()
.add("Access-Control-Allow-Origin", "*");
}
您需要了解 CORS 协议的工作原理。对于简单的 CORS 请求,没有预检,而 non-simple CORS 请求,进行预检请求。预检请求是 prior 实际请求的 OPTIONS 请求,询问服务器是否允许该请求。这个请求是用特定的 headers 发出的,服务器应该用它自己的特定响应 headers 来响应。我在您链接到的 post 中提到了 all。我什至解释了每个 header 的用途。
header 特别是您的问题是预检请求 header Access-Control-Request-Headers
。浏览器将发送此 header(在预检请求中)并且该值将列出客户端尝试设置的所有 header。服务器应以 Access-Control-Allow-Headers
header 响应,其中列出了允许从客户端发送的所有 header。您希望允许客户端发送的任何 header 都应列在此响应 header 的值中。例如
Access-Control-Allow-Headers: Authorization, xy
我还在代码中添加了注释,告诉您可能需要更改列表
response.getHeaders().add("Access-Control-Allow-Headers",
// Whatever other non-standard/safe headers (see list above)
// you want the client to be able to send to the server,
// put it in this list. And remove the ones you don't want.
"X-Requested-With, Authorization, " +
"Accept-Version, Content-MD5, CSRF-Token, Content-Type");
请花时间阅读整个 post 并阅读我链接到的一些资源,以便您可以充分了解 CORS 协议的工作原理。
您需要将 cutsomer header xy 添加到“Access-Control-Allow-Headers”列表。这对我来说很有魅力
containerResponseContext.getHeaders()
.add("Access-Control-Allow-Headers", "origin, content-type, accept, authorization, xy");
客户 header 在飞行前请求期间被阻止。
详细答案可以参考这里:How to handle CORS using JAX-RS with Jersey
我正在使用 Jersey2 响应过滤器来处理来自浏览器的 CORS 请求。在这一点上,它看起来很像 Paul Samsotha 在这个问题中的那个 How to handle CORS using JAX-RS with Jersey
@Provider
public class CORSFilter implements ContainerRequestFilter, ContainerResponseFilter {
@Override
public void filter(ContainerRequestContext containerRequestContext, ContainerResponseContext containerResponseContext)
throws IOException {
if (containerRequestContext.getHeaderString("Origin") == null) {
return;
}
if (isPreflightRequest(containerRequestContext)) {
containerResponseContext.getHeaders()
.add("Access-Control-Allow-Credentials", "true");
containerResponseContext.getHeaders()
.add("Access-Control-Allow-Headers", "origin, content-type, accept, authorization");
containerResponseContext.getHeaders()
.add("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS, HEAD");
}
containerResponseContext.getHeaders()
.add("Access-Control-Allow-Origin", "*");
}
@Override
public void filter(ContainerRequestContext containerRequestContext) throws IOException {
if (isPreflightRequest(containerRequestContext)) {
containerRequestContext.abortWith(Response.ok().build());
return;
}
}
private static boolean isPreflightRequest(ContainerRequestContext request) {
return request.getHeaderString("Origin") != null
&& request.getMethod().equalsIgnoreCase("OPTIONS");
}
}
现在一切正常,直到我像这样在前端添加自己的 header(用于授权目的)
axios.defaults.headers.common["xy"] = "example content";
在服务器端我有一个简单的DELETE方法作为例子
@Path("/example")
@DELETE
public void example(@Context HttpHeaders headers) {
String headerContent = headers.getHeaderString("xy");
...do stuff...
}
添加此 header 后,将不再为主调用执行过滤器。它仍然为预检执行,但不再为 DELETE 调用执行,这会在浏览器中触发 CORS-Error。
从 Postman 对其进行测试完全相同 header。我发出呼叫,过滤器触发,一切正常。但是浏览器一运行,过滤器就不再触发了。
我也对 PUT 进行了测试,结果相同。我用 vanilla JS XMLHTTPRequest 测试了它,结果相同。通过远程调试我知道过滤器根本没有被执行。当我删除 header 时,我仍然有 OPTIONS 和 DELETE 调用,但是对这两个调用都执行了过滤器。当我再次添加 header 时,它只针对 OPTIONS 调用(没有 header)执行,但不会针对 DELETE 调用执行。
这在 Tomcat 9 上运行,Jersey 2.27 和 Java 11。
所以我的问题是,为什么 header 似乎会终止过滤器的执行?
您似乎添加了多个条件来应用过滤器。
尝试禁用以下条件:(containerRequestContext.getHeaderString("Origin") == null)
和 isPreflightRequest(containerRequestContext)
。
例如:
@Override
public void filter(ContainerRequestContext containerRequestContext, ContainerResponseContext containerResponseContext)
throws IOException {
// disable this condition.
//if (containerRequestContext.getHeaderString("Origin") == null) {
// return;
//}
// disable this condition
// it seems problemetic
// if (isPreflightRequest(containerRequestContext)) {
containerResponseContext.getHeaders()
.add("Access-Control-Allow-Credentials", "true");
containerResponseContext.getHeaders()
.add("Access-Control-Allow-Headers", "origin, content-type, accept, authorization");
containerResponseContext.getHeaders()
.add("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS, HEAD");
//}
containerResponseContext.getHeaders()
.add("Access-Control-Allow-Origin", "*");
}
您需要了解 CORS 协议的工作原理。对于简单的 CORS 请求,没有预检,而 non-simple CORS 请求,进行预检请求。预检请求是 prior 实际请求的 OPTIONS 请求,询问服务器是否允许该请求。这个请求是用特定的 headers 发出的,服务器应该用它自己的特定响应 headers 来响应。我在您链接到的 post 中提到了 all。我什至解释了每个 header 的用途。
header 特别是您的问题是预检请求 header Access-Control-Request-Headers
。浏览器将发送此 header(在预检请求中)并且该值将列出客户端尝试设置的所有 header。服务器应以 Access-Control-Allow-Headers
header 响应,其中列出了允许从客户端发送的所有 header。您希望允许客户端发送的任何 header 都应列在此响应 header 的值中。例如
Access-Control-Allow-Headers: Authorization, xy
我还在代码中添加了注释,告诉您可能需要更改列表
response.getHeaders().add("Access-Control-Allow-Headers",
// Whatever other non-standard/safe headers (see list above)
// you want the client to be able to send to the server,
// put it in this list. And remove the ones you don't want.
"X-Requested-With, Authorization, " +
"Accept-Version, Content-MD5, CSRF-Token, Content-Type");
请花时间阅读整个 post 并阅读我链接到的一些资源,以便您可以充分了解 CORS 协议的工作原理。
您需要将 cutsomer header xy 添加到“Access-Control-Allow-Headers”列表。这对我来说很有魅力
containerResponseContext.getHeaders()
.add("Access-Control-Allow-Headers", "origin, content-type, accept, authorization, xy");
客户 header 在飞行前请求期间被阻止。 详细答案可以参考这里:How to handle CORS using JAX-RS with Jersey