我正在处理一个使用嵌入式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网络安全标志启动Chrome来验证这一点。没有发送OPTIONS请求,POST正常工作。
我的想法是,由于它适用于POST,所以这不是授权或安全问题——只是Jetty没有正确配置来处理飞行前请求(它只是返回401)。
我找不到太多关于嵌入式Jetty的文档,也找不到在对setInitParameter的调用中使用哪些CrossOriginFilter常量作为属性键(此外,由于该方法调用的第二个参数是String,我真的不知道如何格式化值)。
我应该在CrossOriginFilter上设置哪些参数来处理OPTIONS请求?如果我在上面说了什么错误的话,或者做了任何错误的假设,请纠正我!我在这方面的经验非常有限。
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");
这不是一个init参数键。(事实上,这是Access-Control-Allow-Credentials
的头名称常量)如果您想允许凭据,那么按照javadoc所说的去做。
holder.setInitParameter(CrossOriginFilter.ALLOW_CREDENTIALS_PARAM, "true");
我通过使用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会发送一个"缓存控制"标头,如果您不允许CORS过滤器使用此标头,则OPTIONS请求将不会用正确的标头进行响应。CrossOriginFilter
联机的大多数示例都不包括此标头。
您可以选择将CHAIN_PREFLIGHT_PARAM
设置为false
(默认值为true
)。如果将其设置为false
,过滤器将响应请求,而不将请求发送到Servlet。如果您想自己处理OPTIONS请求,则无需设置此参数。
更新:我完全尝试了你的代码,但我在上下文处理程序上添加了过滤器,而不是在应用程序上。它是这样工作的。
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));