2014-02-15 20 views
24

我是新来与跨源资源共享工作,并试图让我的web应用程序对CORS请求作出回应。我的webapp是一个运行在Tomcat 7.0.42上的Spring 3.2应用程序。困惑如何处理CORS OPTIONS预检要求

在我的webapp的web.xml,我已经启用了Tomcat的CORS过滤器:

<!-- Enable CORS (cross origin resource sharing) --> 
<!-- http://tomcat.apache.org/tomcat-7.0-doc/config/filter.html#CORS_Filter --> 
<filter> 
    <filter-name>CorsFilter</filter-name> 
    <filter-class>org.apache.catalina.filters.CorsFilter</filter-class> 
</filter> 
<filter-mapping> 
    <filter-name>CorsFilter</filter-name> 
    <url-pattern>/*</url-pattern> 
</filter-mapping> 

我的客户(与AngularJS 1.2.12写的)正试图访问一个REST端点使用基本身份验证功能。当它使得它的GET请求,浏览器首先预检请求,但是从服务器接收403禁止响应:

Request URL:http://dev.mydomain.com/joeV2/users/listUsers 
Request Method:OPTIONS 
Status Code:403 Forbidden 
Request Headers: 
    OPTIONS /joeV2/users/listUsers HTTP/1.1 
    Host: dev.mydomain.com 
    Connection: keep-alive 
    Cache-Control: max-age=0 
    Access-Control-Request-Method: GET 
    Origin: http://localhost:8000 
    User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/32.0.1700.107 Safari/537.36 
    Access-Control-Request-Headers: accept, authorization 
    Accept: */* 
    Referer: http://localhost:8000/ 
    Accept-Encoding: gzip,deflate,sdch 
    Accept-Language: en-US,en;q=0.8 
Response Headers: 
    HTTP/1.1 403 Forbidden 
    Date: Sat, 15 Feb 2014 02:16:05 GMT 
    Content-Type: text/plain; charset=UTF-8 
    Content-Length: 0 
    Connection: close 

我不完全知道如何着手。 The Tomcat filter, by default,接受OPTIONS头来访问资源。

的问题,我认为,这是我的资源(请求URL)http://dev.mydomain.com/joeV2/users/listUsers被配置为只接受GET方法:

@RequestMapping(method=RequestMethod.GET, value="listUsers", produces=MediaType.APPLICATION_JSON_VALUE) 
@ResponseBody 
public List<User> list(){ 
    return userService.findAllUsers(); 
} 

这是否意味着我必须作出这样的方法/端点接受OPTIONS方法好?如果是这样,这是否意味着我必须明确地让每个REST端点接受OPTIONS方法?除了混乱的代码之外,我很困惑这将如何工作。从我所了解的OPTIONS预检中,浏览器可以验证浏览器是否可以访问指定的资源。我的理解是,我的控制器方法甚至不应该在预检期间被调用。因此,将OPTIONS指定为可接受的方法将会适得其反。

应的Tomcat是响应OPTIONS直接要求,甚至没有访问我的代码?如果是这样,我的配置中是否有缺少的东西?

回答

40

我坐了下来,并通过org.apache.catalina.filters.CorsFilter调试找出原因的请求被禁止的。希望这可以帮助未来的人。

根据该W3 CORS Spec Section 6.2 Preflight Requests,预检必须如果提交任何头不匹配所允许的标题拒绝该请求。

default configuration for the CorsFiltercors.allowed.headers(与您的一样)不包括与请求一起提交的Authorization标头。

我更新了cors.allowed.headers过滤器设置,接受authorization标题,并且预检请求现已成功。

<filter> 
    <filter-name>CorsFilter</filter-name> 
    <filter-class>org.apache.catalina.filters.CorsFilter</filter-class> 
    <init-param> 
     <param-name>cors.allowed.headers</param-name> 
     <param-value>Content-Type,X-Requested-With,accept,Origin,Access-Control-Request-Method,Access-Control-Request-Headers,Authorization</param-value> 
    </init-param>  
</filter> 

当然,我不知道为什么authorization头默认情况下不被CORS过滤允许的。

+0

好奇,如果你尝试了我的答案第一位?我使用的是Apache,并且不需要设置'authorization'标头。检查Access Control-Request-Headers设置的'httpProvider'设置。您的获取请求正在等待授权头接受。使用'$ httpProvider.defaults.headers.common = {}''配置你的'$ httpProvider'应该重置它。 (这就是为什么我想知道你是否尝试过)。我更喜欢将我可以用作安全规则的最适用的Tomcat设置。通过CORS设置进行通用可以为您解决问题。 –

+0

@BrianVanderbusch不 - 我实际上没有得到您的回应,直到我完成调试TC并找出问题出在哪里。我也会用你提出的解决方案进行测试,尽管我并不完全确定考虑到授权标题是什么引发了这个问题,这是否重要。在我的情况下,我需要授权头,因为它是一个REST端点,我需要为每个请求进行认证。 –

+1

@Eric这里有点晚,但'授权'标题实际上不应该伴随预检请求,根据规范[这里](http://www.w3.org/TR/cors/#cross-origin) - 请求与 - 预检-0)。预检的要点纯粹是为了确保方法(您的情况下为GET)和标头有效发送到服务器。没有授权/认证 –

4

我想尝试的第一件事就是设置你的共同标题为您HTTP请求的角度调度,通过模块上插入下面的配置块:

.config(function($httpProvider){ 
    $httpProvider.defaults.headers.common = {}; 
    $httpProvider.defaults.headers.post = {}; 
    $httpProvider.defaults.headers.put = {}; 
    $httpProvider.defaults.headers.patch = {}; 
}) 

这些都应该是在默认情况下已经设置,但我发现我经常必须在我的配置中手动执行此操作,因为来自其他模块的任何重写或内部角引导过程。

您的CORS过滤器应该在服务器端足以允许这些类型的请求,但有时您需要指定请求方法以及您的来源以及接受的内容类型。 tomcat文档有这个高级块,它解决了这些问题。

<filter> 
    <filter-name>CorsFilter</filter-name> 
    <filter-class>org.apache.catalina.filters.CorsFilter</filter-class> 
    <init-param> 
    <param-name>cors.allowed.origins</param-name> 
    <param-value>*</param-value> 
    </init-param> 
    <init-param> 
    <param-name>cors.allowed.methods</param-name> 
    <param-value>GET,POST,HEAD,OPTIONS,PUT</param-value> 
    </init-param> 
    <init-param> 
    <param-name>cors.allowed.headers</param-name> 
    <param-value>Content-Type,X-Requested-With,accept,Origin,Access-Control-Request-Method,Access-Control-Request-Headers</param-value> 
    </init-param> 
    <init-param> 
    <param-name>cors.exposed.headers</param-name> 
    <param-value>Access-Control-Allow-Origin,Access-Control-Allow-Credentials</param-value> 
    </init-param> 
    <init-param> 
    <param-name>cors.support.credentials</param-name> 
    <param-value>true</param-value> 
    </init-param> 
    <init-param> 
    <param-name>cors.preflight.maxage</param-name> 
    <param-value>10</param-value> 
    </init-param> 
</filter> 
<filter-mapping> 
    <filter-name>CorsFilter</filter-name> 
    <url-pattern>/*</url-pattern> 
</filter-mapping> 

如果第一个它自己不工作,尽量提高你的过滤器,尤其是:

<init-param> 
     <param-name>cors.allowed.methods</param-name> 
     <param-value>GET,POST,HEAD,OPTIONS,PUT</param-value> 
     </init-param> 
     <init-param> 
     <param-name>cors.allowed.headers</param-name> 
     <param-value>Content-Type,X-Requested-With,accept,Origin,Access-Control-Request-Method,Access-Control-Request-Headers</param-value> 
     </init-param> 
     <init-param> 
     <param-name>cors.exposed.headers</param-name> 
     <param-value>Access-Control-Allow-Origin,Access-Control-Allow-Credentials</param-value> 
     </init-param> 
+0

这帮了我。谢谢! –