自定义 HTTP 标头块 Jersey CORS 过滤器

Posted

技术标签:

【中文标题】自定义 HTTP 标头块 Jersey CORS 过滤器【英文标题】:Custom HTTP Header blocks Jersey CORS Filter 【发布时间】:2021-05-28 00:00:57 【问题描述】:

我正在使用 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");
  

现在一切正常,直到我像这样在前端添加自己的标头(用于授权目的)

axios.defaults.headers.common["xy"] = "example content";

在服务器端我有一个简单的DELETE方法作为例子

  @Path("/example")
  @DELETE
  public void example(@Context HttpHeaders headers) 
    String headerContent = headers.getHeaderString("xy");
    ...do stuff...
  

添加此标头后,不再为主调用执行过滤器。它仍然为预检执行,但不再为 DELETE 调用执行,这会在浏览器中触发 CORS-Error。

从 Postman 测试它在完全相同的标题下工作得非常好。我拨打电话,过滤器触发,一切正常。但是在浏览器运行的那一刻,过滤器就不再被触发了。

我也对 PUT 进行了测试,结果相同。我已经用 vanilla JS XMLHTTPRequest 对其进行了测试,结果相同。通过远程调试我知道过滤器根本没有执行。当我删除标题时,我仍然有 OPTIONS 和 DELETE 调用,但过滤器对这两个调用都执行。当我再次添加标头时,它只对 OPTIONS 调用(没有标头)执行,而不是对 DELETE 调用。

这在 Tomcat 9 上运行,带有 Jersey 2.27 和 Java 11。

所以我的问题是,为什么标头似乎会杀死过滤器的执行?

【问题讨论】:

会不会是 XMLHTTPRequest 没有发送Origin 标头? 【参考方案1】:

您需要了解 CORS 协议的工作原理。对于简单的 CORS 请求,没有预检,非简单的 CORS 请求会发出预检请求。预检请求是在实际请求之前发出的 OPTIONS 请求,询问服务器是否允许该请求。此请求使用特定的标头进行,服务器应使用其自己的特定响应标头进行响应。我在您链接到的帖子中提到了所有。我什至解释了每个标题的用途。

特别是您的问题的标头是预检请求标头Access-Control-Request-Headers。浏览器将发送此标头(在预检请求中),该值将列出客户端尝试设置的所有标头。服务器应使用Access-Control-Allow-Headers 标头进行响应,该标头列出了允许从客户端发送的所有标头。您希望允许客户端发送的任何标头都应列在此响应标头的值中。例如

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");

请花时间查看整篇文章并阅读我链接到的一些资源,以便您充分了解 CORS 协议的工作原理。

【讨论】:

你说得对,谢谢。由于我对这个话题缺乏兴趣,我只是过度使用它。我只是在观察“Access-Control-Allow-Origin”标头设置为“*”,而不在意其他明显的设置。我身边的经典催款克鲁格效应。【参考方案2】:

您似乎添加了多个条件来应用过滤器。

尝试禁用以下条件:(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", "*");

  

【讨论】:

以上是关于自定义 HTTP 标头块 Jersey CORS 过滤器的主要内容,如果未能解决你的问题,请参考以下文章

自定义 http 标头破坏了 CORS

由 CORS 阻止的 Spring Boot 自定义响应标头

如何将 CORS 标头添加到 Jersey 2 中灰熊服务器的 StaticHttpHandler 提供的响应?

无法使用 spring mvc 和 $http 访问 CORS 环境中的自定义标头

AJAX 使用 CORS 获取自定义响应标头

带有 CORS 和自定义标头的 AngularJS