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