如何配置嵌入式 Jetty 以处理 OPTIONS 预检请求?

Posted

技术标签:

【中文标题】如何配置嵌入式 Jetty 以处理 OPTIONS 预检请求?【英文标题】:How do I configure embedded Jetty to handle OPTIONS preflight requests? 【发布时间】:2014-11-19 07:04:17 【问题描述】:

我正在开发一个使用嵌入式 Jetty 的项目(不幸的是,我只是“继承”了项目的服务器端,对 Jetty 的使用及其配置不是很熟悉)。

突然出现一个奇怪的案例 - 我会尽力描述:

基于 Web 的 UI(使用来自不同域的 AngularJS,因此使用了 CORS)发送 POST 请求以更改服务器上某些内容的状态。这在过去的某个时候有效(它最后一次使用可能是在一个月左右之前)。

昨天这停止了工作。检查 REST 调用,我看到首先发出了一个 OPTIONS 请求。 POST 的内容类型是 application/json,所以根据我的阅读,这是正确的。我不确定为什么之前没有发送它 - 公司最近更新了 Chrome 版本,而旧版本没有发送预检请求,但这只是猜测。无论如何,我认为这是我的应用程序中用于为 CORS 配置 Jetty 的相关代码:

FilterHolder holder = new FilterHolder(new CrossOriginFilter());
holder.setInitParameter(CrossOriginFilter.ALLOWED_ORIGINS_PARAM,  "*");
holder.setInitParameter(CrossOriginFilter.ACCESS_CONTROL_ALLOW_CREDENTIALS_HEADER, "true");
appHandler.addFilter(holder, "/*", EnumSet.of(DispatcherType.REQUEST));

POST 请求一切正常。我可以通过使用 --disable-web-security 标志启动 Chrome 来验证这一点。未发送任何 OPTIONS 请求,POST 正常工作。

我的想法是,因为它适用于 POST,所以它不是授权或安全问题 - 只是 Jetty 没有正确配置来处理预检请求(它只是返回 401)。

我找不到太多关于嵌入式 Jetty 的文档,以及哪些 CrossOriginFilter 常量在调用 setInitParameter 时用作属性键(此外,由于该方法调用的第二个参数是一个字符串,我真的没有了解如何格式化这些值)。

我应该在 CrossOriginFilter 上设置哪些参数来处理 OPTIONS 请求?如果我在上面说了什么错误或做出了任何错误的假设,请纠正我!我在这方面的经验非常有限。

【问题讨论】:

你解决了这个问题吗?如果是,请在这里提供答案吗? 【参考方案1】:

CrossOriginFilter 的文档:

http://www.eclipse.org/jetty/documentation/current/cross-origin-filter.html

CrossOriginFilter 的 Javadoc:

http://download.eclipse.org/jetty/stable-9/apidocs/org/eclipse/jetty/servlets/CrossOriginFilter.html

实际源代码:(有时这也有助于人们理解):

https://github.com/eclipse/jetty.project/blob/jetty-9.2.3.v20140905/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/CrossOriginFilter.java

简而言之,您可能希望将 OPTIONS 添加到允许的方法中。

(就像 javadoc 说的那样)

FilterHolder holder = new FilterHolder(new CrossOriginFilter());
holder.setInitParameter(CrossOriginFilter.ALLOWED_METHODS_PARAM, "GET,POST,HEAD,OPTIONS");
appHandler.addFilter(holder, "/*", EnumSet.of(DispatcherType.REQUEST));

现在,要解决您遇到的另一个错误...

这没有任何作用...

holder.setInitParameter(CrossOriginFilter.ACCESS_CONTROL_ALLOW_CREDENTIALS_HEADER,
  "true");

这不是初始化参数键。 (事实上​​,这是Access-Control-Allow-Credentials 的标头名称常量)如果您想允许凭据,则按照 javadoc 的说明进行操作。

holder.setInitParameter(CrossOriginFilter.ALLOW_CREDENTIALS_PARAM, "true");

【讨论】:

谢谢 - 我已经找到了,但我仍然不知道为什么我会收到 401 响应 OPTIONS 请求。 我不确定从技术上讲 OPTIONS 请求是否属于 CORS 的“保护伞”,但当然,OPTIONS 请求仍然来自不同的域(因为 POST 是从不同的域尝试的)领域)。我不确定该代码应该显示什么。只是说为了成为预检请求,方法必须是 OPTIONS。我可能遗漏了一些东西,但没有任何东西被拒绝 - 如果有的话, OPTIONS 的所有东西都被拒绝了。 您可以使用CrossOriginFilter.ALLOWED_METHODS_PARAM 代替allowedMethods【参考方案2】:

我通过对 FilterHolder 使用以下配置解决了这个问题:

FilterHolder cors = new FilterHolder(CrossOriginFilter.class);
cors.setInitParameter(CrossOriginFilter.ALLOWED_ORIGINS_PARAM, "*");
cors.setInitParameter(CrossOriginFilter.ACCESS_CONTROL_ALLOW_ORIGIN_HEADER, "*");
cors.setInitParameter(CrossOriginFilter.ALLOWED_METHODS_PARAM, "OPTIONS,GET,POST,HEAD");
cors.setInitParameter(CrossOriginFilter.ALLOWED_HEADERS_PARAM, "X-Requested-With,Content-Type,Accept,Origin,Cache-Control");
cors.setInitParameter(CrossOriginFilter.CHAIN_PREFLIGHT_PARAM, "false");

Chrome 会发送一个“Cache-Control”标头,如果您的 CORS 过滤器不允许此标头,则 OPTIONS 请求将不会以正确的标头响应。大多数CrossOriginFilter 在线示例不包含此标头。

您可以选择将CHAIN_PREFLIGHT_PARAM 设置为false(默认为true)。如果将其设置为false,过滤器将响应请求而不将请求发送到Servlet。如果您想自己处理 OPTIONS 请求,则无需设置此参数。

【讨论】:

【参考方案3】:

更新:我完全尝试了您的代码,但我在上下文处理程序上添加了过滤器,而不是在应用程序上。它是这样工作的。

        FilterHolder holder = new FilterHolder(new CrossOriginFilter());
    holder.setInitParameter(CrossOriginFilter.ALLOWED_ORIGINS_PARAM,  "http://localhost:8100");
    holder.setInitParameter(CrossOriginFilter.ACCESS_CONTROL_ALLOW_CREDENTIALS_HEADER, "true");

    contextHandler.addFilter(holder, "/*", EnumSet.of(DispatcherType.REQUEST)); 

【讨论】:

抱歉,我没有包含足够的代码。 appHandler ServletContextHandler 似乎:ServletContextHandler appHandler = new ServletContextHandler(ServletContextHandler.SESSIONS);

以上是关于如何配置嵌入式 Jetty 以处理 OPTIONS 预检请求?的主要内容,如果未能解决你的问题,请参考以下文章

jetty:如何以编程方式配置多个虚拟主机?

Jetty 9.0 嵌入了带有 SPDY 但没有 SSL/NPN 的配置

嵌入式Jetty:http请求到达后如何运行安全处理程序?

如何在 Django 中配置 X-Frame-Options 以允许 iframe 嵌入一个视图?

Jetty vs. Tomcat

如何选择Tomcat和Jetty