26

我是使用跨域资源共享并试图让我的 web 应用程序响应 CORS 请求的新手。我的 webapp 是在 Tomcat 7.0.42 上运行的 Spring 3.2 应用程序。

在我的 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 请求时,Chrome 会首先对请求进行预检,但会收到来自服务器的 403 Forbidden 响应:

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

我不完全确定如何进行。 默认情况下,Tomcat 过滤器接受 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 请求而不访问我的代码?如果是这样,我的配置中是否缺少某些内容?

4

2 回答 2

48

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

根据W3 CORS 规范第 6.2 节 Preflight Requests,如果提交的任何标头与允许的标头不匹配,则预检必须拒绝请求。

CorsFilter 的默认配置cors.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>

当然,我不确定为什么authorizationCORS 过滤器默认不允许标头。

于 2014-02-15T03:47:45.987 回答
4

我要尝试的第一件事是通过在您的模块上插入以下配置块来为您的 Angular 调度的 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>
于 2014-02-15T03:08:46.733 回答