web-dev-qa-db-ja.com

CORS OPTIONSプリフライトリクエストの処理方法について混乱している

Cross Origin Resource Sharingを使用して、webappが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リクエストに直接応答する必要がありますか?もしそうなら、私の設定に何か欠けているものはありますか?

24
Eric B.

私は座ってorg.Apache.catalina.filters.CorsFilterをデバッグし、リクエストが禁止されている理由を見つけました。これが将来誰かを助けてくれることを願っています。

W3 CORS Spec Section 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>

もちろん、デフォルトでauthorizationヘッダーがCORSフィルターで許可されていない理由はわかりません。

43
Eric B.

私が最初に試みることは、モジュールに次の構成ブロックを挿入することにより、angularディスパッチするhttp要求の共通ヘッダーを設定することです。

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

これらはデフォルトですでに設定されているはずですが、他のモジュールまたは内部angular bootstrapingプロセスからのオーバーライドのために、私はしばしば設定でこれを手動で行う必要があることがわかりました。

CORSフィルタshouldは、これらのタイプのリクエストを許可するのにサーバー側で十分ですが、場合によっては、オリジンと受け入れられたコンテンツタイプに加えてリクエストメソッドを指定する必要があります。 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>
4