Ответ 1
Примечание: обязательно прочитайте ОБНОВЛЕНИЕ внизу
@CrossOriginResourceSharing
- это аннотация CXF, поэтому она не будет работать с Джерси.
С Джерси для обработки CORS я обычно просто использую ContainerResponseFilter
. ContainerResponseFilter
для Джерси 1 и 2 немного отличается. Поскольку вы не упомянули, какую версию вы используете, я выложу обе.
Джерси 2
import java.io.IOException;
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerResponseContext;
import javax.ws.rs.container.ContainerResponseFilter;
@Provider
public class CORSFilter implements ContainerResponseFilter {
@Override
public void filter(ContainerRequestContext request,
ContainerResponseContext response) throws IOException {
response.getHeaders().add("Access-Control-Allow-Origin", "*");
response.getHeaders().add("Access-Control-Allow-Headers",
"origin, content-type, accept, authorization");
response.getHeaders().add("Access-Control-Allow-Credentials", "true");
response.getHeaders().add("Access-Control-Allow-Methods",
"GET, POST, PUT, DELETE, OPTIONS, HEAD");
}
}
Если вы используете сканирование пакетов для обнаружения поставщиков и ресурсов, аннотация @Provider
должна позаботиться о конфигурации для вас. Если нет, то вам нужно будет явно зарегистрировать его с помощью ResourceConfig
или подкласса Application
.
Пример кода для явной регистрации фильтра в ResourceConfig
:
final ResourceConfig resourceConfig = new ResourceConfig();
resourceConfig.register(new CORSFilter());
final final URI uri = ...;
final HttpServer httpServer = GrizzlyHttpServerFactory.createHttpServer(uri, resourceConfig);
Для Jersey 2.x, если у вас возникают проблемы при регистрации этого фильтра, вот пара ресурсов, которые могут помочь
Джерси 1
import com.sun.jersey.spi.container.ContainerRequest;
import com.sun.jersey.spi.container.ContainerResponse;
import com.sun.jersey.spi.container.ContainerResponseFilter;
public class CORSFilter implements ContainerResponseFilter {
@Override
public ContainerResponse filter(ContainerRequest request,
ContainerResponse response) {
response.getHttpHeaders().add("Access-Control-Allow-Origin", "*");
response.getHttpHeaders().add("Access-Control-Allow-Headers",
"origin, content-type, accept, authorization");
response.getHttpHeaders().add("Access-Control-Allow-Credentials", "true");
response.getHttpHeaders().add("Access-Control-Allow-Methods",
"GET, POST, PUT, DELETE, OPTIONS, HEAD");
return response;
}
}
Конфигурация web.xml, вы можете использовать
<init-param>
<param-name>com.sun.jersey.spi.container.ContainerResponseFilters</param-name>
<param-value>com.yourpackage.CORSFilter</param-value>
</init-param>
Или ResourceConfig
вы можете сделать
resourceConfig.getContainerResponseFilters().add(new CORSFilter());
Или сканирование пакетов с аннотацией @Provider
.
РЕДАКТИРОВАТЬ
Обратите внимание, что приведенный выше пример может быть улучшен. Вам нужно будет узнать больше о том, как работает CORS. Пожалуйста, смотрите здесь. Например, вы получите заголовки для всех ответов. Это может быть нежелательно. Возможно, вам просто нужно обработать предпечатную проверку (или ОПЦИИ). Если вы хотите увидеть улучшенный реализованный фильтр CORS, вы можете проверить исходный код RESTeasy CorsFilter
ОБНОВИТЬ
Поэтому я решил добавить более правильную реализацию. Вышеуказанная реализация является ленивой и добавляет все заголовки CORS ко всем запросам. Другая ошибка заключается в том, что, поскольку это всего лишь фильтр ответа, запрос все еще обрабатывается. Это означает, что при поступлении предварительного запроса, который является запросом OPTIONS, метод OPTIONS не будет реализован, поэтому мы получим ответ 405, что неверно.
Вот как это должно работать. Таким образом, существует два типа запросов CORS: простые запросы и запросы перед проверкой. Для простого запроса браузер отправит фактический запрос и добавит заголовок запроса Origin
. Браузер ожидает, что ответ будет иметь заголовок Access-Control-Allow-Origin
, сообщая, что источник из заголовка Origin
разрешен. Чтобы его можно было считать "простым запросом", он должен соответствовать следующим критериям:
- Будьте одним из следующих методов:
- ПОЛУЧИТЬ
- ГОЛОВА
- СООБЩЕНИЕ
- Помимо заголовков, автоматически устанавливаемых браузером, запрос может содержать только следующие заголовки, установленные вручную:
-
Accept
-
Accept-Language
-
Content-Language
-
Content-Type
-
DPR
-
Save-Data
-
Viewport-Width
-
Width
-
- Единственные допустимые значения для заголовка
Content-Type
:-
application/x-www-form-urlencoded
-
multipart/form-data
-
text/plain
-
Если запрос не удовлетворяет всем этим трем критериям, выполняется предпечатная проверка. Это запрос OPTIONS, который делается на сервер до фактического выполнения запроса. Он будет содержать разные заголовки Access-Control-XX-XX
, и сервер должен отвечать на эти заголовки своими собственными заголовками ответа CORS. Вот соответствующие заголовки:
Preflight Request and Response Headers
+-----------------------------------+--------------------------------------+
| REQUEST HEADER | RESPONSE HEADER |
+===================================+======================================+
| Origin | Access-Control-Allow-Origin |
+-----------------------------------+--------------------------------------+
| Access-Control-Request-Headers | Access-Control-Allow-Headers |
+-----------------------------------+--------------------------------------+
| Access-Control-Request-Method | Access-Control-Allow-Methods |
+-----------------------------------+--------------------------------------+
| XHR.withCredentials | Access-Control-Allow-Credentials |
+-----------------------------------+--------------------------------------+
-
С
Origin
заголовка запроса, то значение будет домен происхождения сервера, и ответAccess-Control-Allow-Header
должен быть тот же самый адрес или*
, чтобы указать, что все корни разрешены. -
Если клиент пытается вручную установить какие-либо заголовки, отсутствующие в приведенном выше списке, браузер установит заголовок
Access-Control-Request-Headers
, при этом значением будет список всех заголовков, которые пытается установить клиент. Сервер должен ответить обратно сAccess-Control-Allow-Headers
ответаAccess-Control-Allow-Headers
, со значением, являющимся списком разрешенных заголовков. -
Браузер также установит заголовок запроса
Access-Control-Request-Method
, значением которого будет HTTP-метод запроса. Сервер должен ответить заголовком ответаAccess-Control-Allow-Methods
, со значением, являющимся списком методов, которые он допускает. -
Если клиент использует
XHR.withCredentials
, сервер должен ответить заголовком ответаAccess-Control-Allow-Credentials
со значениемtrue
. Узнайте больше здесь.
Итак, со всем сказанным, вот лучшая реализация. Несмотря на то, что это лучше, чем приведенная выше реализация, она все же уступает RESTEasy, с которой я связан, поскольку эта реализация по-прежнему допускает все источники. Но этот фильтр лучше выполняет требования спецификации CORS, чем вышеупомянутый фильтр, который просто добавляет заголовки ответа CORS ко всем запросам. Обратите внимание, что вам также может понадобиться изменить Access-Control-Allow-Headers
для соответствия заголовкам, которые разрешит ваше приложение; Вы можете добавить или удалить некоторые заголовки из списка в этом примере.
@Provider
@PreMatching
public class CorsFilter implements ContainerRequestFilter, ContainerResponseFilter {
/**
* Method for ContainerRequestFilter.
*/
@Override
public void filter(ContainerRequestContext request) throws IOException {
// If it a preflight request, we abort the request with
// a 200 status, and the CORS headers are added in the
// response filter method below.
if (isPreflightRequest(request)) {
request.abortWith(Response.ok().build());
return;
}
}
/**
* A preflight request is an OPTIONS request
* with an Origin header.
*/
private static boolean isPreflightRequest(ContainerRequestContext request) {
return request.getHeaderString("Origin") != null
&& request.getMethod().equalsIgnoreCase("OPTIONS");
}
/**
* Method for ContainerResponseFilter.
*/
@Override
public void filter(ContainerRequestContext request, ContainerResponseContext response)
throws IOException {
// if there is no Origin header, then it is not a
// cross origin request. We don't do anything.
if (request.getHeaderString("Origin") == null) {
return;
}
// If it is a preflight request, then we add all
// the CORS headers here.
if (isPreflightRequest(request)) {
response.getHeaders().add("Access-Control-Allow-Credentials", "true");
response.getHeaders().add("Access-Control-Allow-Methods",
"GET, POST, PUT, DELETE, OPTIONS, HEAD");
response.getHeaders().add("Access-Control-Allow-Headers",
// Whatever other non-standard/safe headers (see list above)
// you want the client to be able to send to the server,
// put it in this list. And remove the ones you don't want.
"X-Requested-With, Authorization, " +
"Accept-Version, Content-MD5, CSRF-Token");
}
// Cross origin requests can be either simple requests
// or preflight request. We need to add this header
// to both type of requests. Only preflight requests
// need the previously added headers.
response.getHeaders().add("Access-Control-Allow-Origin", "*");
}
}
Чтобы узнать больше о CORS, я предлагаю прочитать MDN-документы по обмену ресурсами между источниками (CORS)