Подтвердить что ты не робот

Смутно о том, как обращаться с запросами preflight CORS OPTIONS

Я новичок в работе с Cross Origin Resource Sharing и пытаюсь заставить мой webapp отвечать на запросы CORS. Мой webapp - это приложение Spring 3.2, работающее на Tomcat 7.0.42.

В моем 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 запрещенный ответ с сервера:

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 напрямую, даже не обращаясь к моему коду? Если да, то что-то не хватает в моей конфигурации?

4b9b3361

Ответ 1

Я сел и отладил через 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.

Ответ 2

Первое, что я хотел бы попробовать, - установить общие заголовки для ваших HTTP-запросов, отправляемых angular, вставив в свой модуль следующий блок конфигурации:

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

Они должны быть установлены по умолчанию уже, но я обнаружил, что мне часто приходится делать это вручную в моем конфиге из-за каких-либо переопределений из других модулей или внутреннего процесса загрузки angular.

Фильтр 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>