【问题标题】:Confused about how to handle CORS OPTIONS preflight requests对如何处理 CORS OPTIONS 预检请求感到困惑
【发布时间】:2014-03-14 14:31:19
【问题描述】:

我不熟悉使用跨域资源共享并试图让我的 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

我不完全确定如何继续。 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 请求而不访问我的代码?如果是这样,我的配置中是否缺少某些内容?

【问题讨论】:

    标签: angularjs tomcat spring-mvc cors preflight


    【解决方案1】:

    我要尝试的第一件事是通过在您的模块上插入以下配置块来为您的 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>
    

    【讨论】:

    • 这对我有帮助。谢谢!
    【解决方案2】:

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

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

    default configuration for the 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>
    

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

    【讨论】:

    • 好奇你是否尝试过我的回答?我使用的是 apache,不需要设置 authorization 标头。检查您的 $httpProvider 设置以获取 Access-Control-Request-Headers 设置。您的 get 请求期望 authorization 标头接受。用$httpProvider.defaults.headers.common = {} 配置你的$httpProvider 应该 重置它。 (这就是为什么我想知道你是否尝试过)。我更喜欢将最适用的 Tomcat 设置作为安全规则。对你的 CORS 设置进行通用设置可能会让你在未来遇到问题。
    • @BrianVanderbusch 不 - 实际上,在我完成 TC 调试并找出问题所在之前,我还没有收到您的回复。我也将使用您提出的解决方案进行测试,尽管考虑到authorization 标头是触发问题的原因,我不完全确定这是否重要。就我而言,我需要 authorization 标头,因为它是一个 REST 端点,我需要对每个请求进行身份验证。
    • @Eric 这里有点晚了,但根据规范hereAuthorization 标头实际上不应该伴随预检请求。预检的目的纯粹是为了确保方法(在您的情况下为 GET)和标头可以有效地发送到服务器。不应发生授权/身份验证
    • @mattedgod 如果将 Auth 标头与预检请求一起发送,这是否意味着它是浏览器问题?我正在使用 Chrome。
    • @EricB。我很惊讶 Chrome 会这样做,但我的理解是这些预检请求是由浏览器处理的,而不是由发出请求的库处理的。换句话说,jQuery 不必处理发出预检请求,它相信浏览器会这样做。但是,在我在 OS X 上的 Chrome (35) 版本上,没有通过任何 Auth 标头。可能是因为您的authorization 是小写的吗?看起来很奇怪,但我没有其他解释
    猜你喜欢
    • 1970-01-01
    • 2019-08-29
    • 2021-10-22
    • 2021-02-04
    • 2013-01-20
    • 1970-01-01
    • 2016-02-17
    • 2015-08-04
    • 1970-01-01
    相关资源
    最近更新 更多