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

Как обращаться с CORS с использованием JAX-RS с Джерси

Я разрабатываю клиентское приложение java script, на стороне сервера мне нужно обрабатывать CORS, все службы, которые я написал в JAX-RS с JERSEY. Мой код:

@CrossOriginResourceSharing(allowAllOrigins = true)
@GET
@Path("/readOthersCalendar")
@Produces("application/json")
public Response readOthersCalendar(String dataJson) throws Exception {  
     //my code. Edited by gimbal2 to fix formatting
     return Response.status(status).entity(jsonResponse).header("Access-Control-Allow-Origin", "*").build();
}

На данный момент я получаю сообщение об ошибке. Отсутствует заголовок "Access-Control-Allow-Origin" на запрошенном ресурсе. Origin 'http://localhost:8080', следовательно, не допускается."

Пожалуйста, помогите мне с этим.

Спасибо и с уважением Будда Пунеет

4b9b3361

Ответ 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)

Ответ 2

Другой ответ может быть строго правильным, но вводить в заблуждение. Недостающая часть состоит в том, что вы можете смешивать фильтры из разных источников вместе. Даже если Джерси не предоставит фильтр CORS (не факт, который я проверил, но я доверяю другому ответу на это), вы можете использовать tomcat собственный фильтр CORS.

Я использую его с Джерси. У меня есть собственная реализация фильтра базовой проверки подлинности, например, вместе с CORS. Лучший из всех, фильтр CORS настраивается в веб-XML, а не в коде.

Ответ 3

Чтобы решить эту проблему для моего проекта, я использовал ответ Micheal и пришел к такому:

    <plugin>
        <groupId>org.apache.tomcat.maven</groupId>
        <artifactId>tomcat7-maven-plugin</artifactId>
        <version>2.2</version>
        <executions>
            <execution>
                <id>run-embedded</id>
                <goals>
                    <goal>run</goal>
                </goals>
                <phase>pre-integration-test</phase>
                <configuration>
                    <port>${maven.tomcat.port}</port>
                    <useSeparateTomcatClassLoader>true</useSeparateTomcatClassLoader>
                    <contextFile>${project.basedir}/tomcat/context.xml</contextFile>
                    <!--enable CORS for development purposes only. The web.xml file specified is a copy of
                        the auto generated web.xml with the additional CORS filter added -->
                    <tomcatWebXml>${maven.tomcat.web-xml.file}</tomcatWebXml>
                </configuration>
            </execution>
        </executions>
    </plugin>

Фильтр CORS является основным фильтром примера из сайта tomcat.

Edit:
Переменная maven.tomcat.web-xml.file представляет собой свойство pom defined для проекта и содержит путь к файлу web.xml(расположенному в моем проекте)

Ответ 4

Ответ peeskillet правильный. Но я получаю эту ошибку при обновлении веб-страницы (она работает только при первой загрузке):

The 'Access-Control-Allow-Origin' header contains multiple values '*, *', but only one is allowed. Origin 'http://127.0.0.1:8080' is therefore not allowed access.

Поэтому вместо того, чтобы использовать метод add для добавления заголовков для ответа, я использую метод put. Это мой класс

public class MCORSFilter implements ContainerResponseFilter {
    public static final String ACCESS_CONTROL_ALLOW_ORIGIN = "Access-Control-Allow-Origin";
    public static final String ACCESS_CONTROL_ALLOW_ORIGIN_VALUE = "*";

    private static final String ACCESS_CONTROL_ALLOW_CREDENTIALS = "Access-Control-Allow-Credentials";
    private static final String ACCESS_CONTROL_ALLOW_CREDENTIALS_VALUE = "true";

    public static final String ACCESS_CONTROL_ALLOW_HEADERS = "Access-Control-Allow-Headers";
    public static final String ACCESS_CONTROL_ALLOW_HEADERS_VALUE = "Cache-Control, Pragma, Origin, Authorization, Content-Type, X-Requested-With, Accept";

    public static final String ACCESS_CONTROL_ALLOW_METHODS = "Access-Control-Allow-Methods";
    public static final String ACCESS_CONTROL_ALLOW_METHODS_VALUE = "GET, POST, PUT, DELETE, OPTIONS, HEAD";

    public static final String[] ALL_HEADERs = {
            ACCESS_CONTROL_ALLOW_ORIGIN,
            ACCESS_CONTROL_ALLOW_CREDENTIALS,
            ACCESS_CONTROL_ALLOW_HEADERS,
            ACCESS_CONTROL_ALLOW_METHODS
    };
    public static final String[] ALL_HEADER_VALUEs = {
            ACCESS_CONTROL_ALLOW_ORIGIN_VALUE,
            ACCESS_CONTROL_ALLOW_CREDENTIALS_VALUE,
            ACCESS_CONTROL_ALLOW_HEADERS_VALUE,
            ACCESS_CONTROL_ALLOW_METHODS_VALUE
    };
    @Override
    public ContainerResponse filter(ContainerRequest request, ContainerResponse response) {
        for (int i = 0; i < ALL_HEADERs.length; i++) {
            ArrayList<Object> value = new ArrayList<>();
            value.add(ALL_HEADER_VALUEs[i]);
            response.getHttpHeaders().put(ALL_HEADERs[i], value); //using put method
        }
        return response;
    }
}

И добавьте этот класс в init-param в web.xml

<init-param>
            <param-name>com.sun.jersey.spi.container.ContainerResponseFilters</param-name>
            <param-value>com.yourpackage.MCORSFilter</param-value>
        </init-param>

Ответ 5

Удалить аннотацию "@CrossOriginResourceSharing(allowAllOrigins = true)"

Затем возвращаем ответ, как показано ниже:

return Response.ok()
               .entity(jsonResponse)
               .header("Access-Control-Allow-Origin", "*")
               .build();

Но jsonResponse должен заменить объект POJO!

Ответ 6

Просто к вашему сведению, я использовал jax-rs jersey 2, и мне нужно было разрешить все запросы для моего RestApi. Фильтр Tomcat CORS, ответ от Krizka действительно помог мне решить мою проблему, так как я настроил файл web.xml в своем каталоге tomcat (apache tomcat 8). Использовал угловой 6, чтобы делать запросы к моему API.

Ответ 7

Используя JAX-RS, вы можете просто добавить аннотацию @CrossOrigin(origin = yourURL) к вашему контроллеру ресурсов. В вашем случае будет @CrossOrigin(origin = "http://localhost:8080"), но вы также можете использовать @CrossOrigin(origin = "*"), чтобы разрешить любой запрос проходить через ваш веб-сервис.
Вы можете проверить ЭТО для получения дополнительной информации.