Как настроить встроенный Jetty для обработки предварительных запросов OPTIONS?

Я работаю над проектом, в котором используется встроенный Jetty (к сожалению, я просто «унаследовал» серверную часть проекта и не очень хорошо знаком с использованием Jetty и его конфигурацией).

Только что появился странный случай - я сделаю все возможное, чтобы описать:

Веб-интерфейс (с использованием AngularJS из другого домена, поэтому используется CORS) отправляет запрос POST, чтобы изменить состояние чего-либо на сервере. Это работало в какой-то момент в прошлом (последний раз оно использовалось, вероятно, месяц назад).

Вчера это перестало работать. Проверяя вызовы REST, я увидел, что сначала выполняется запрос OPTIONS. Тип содержимого POST — application/json, поэтому, исходя из того, что я прочитал, это правильно. Я не уверен, почему он ранее не был отправлен — возможно, компания недавно обновила свою версию Chrome, и старая версия не отправляла предварительные запросы, но это только предположение. В любом случае, вот, что я думаю, является соответствующим кодом в моем приложении для настройки Jetty для CORS:

FilterHolder holder = new FilterHolder(new CrossOriginFilter());
holder.setInitParameter(CrossOriginFilter.ALLOWED_ORIGINS_PARAM,  "*");
holder.setInitParameter(CrossOriginFilter.ACCESS_CONTROL_ALLOW_CREDENTIALS_HEADER, "true");
appHandler.addFilter(holder, "/*", EnumSet.of(DispatcherType.REQUEST));

Все отлично работает для POST-запросов. Я могу убедиться в этом, запустив Chrome с флагом --disable-web-security. Запрос OPTIONS не отправляется, а POST работает как надо.

Я думаю, что, поскольку он работает для POST, это не проблема авторизации или безопасности - просто Jetty не настроен должным образом для обработки запроса предварительной проверки (он просто возвращает 401).

Я не могу найти много документации для встроенного Jetty и какие из констант CrossOriginFilter использовать в качестве ключей свойств в вызовах setInitParameter (и, кроме того, поскольку 2-й аргумент этого вызова метода является строкой, я действительно понятия не имею, как отформатировать значения).

Какие параметры следует установить в CrossOriginFilter для обработки запросов свойств OPTIONS? И если я сказал выше что-то неправильное или сделал какие-то неверные предположения, пожалуйста, поправьте меня! У меня очень ограниченный опыт в этом.


person Jer    schedule 24.09.2014    source источник
comment
Вы решили эту проблему? Если да, не могли бы вы дать ответ здесь?   -  person sag    schedule 07.10.2015


Ответы (3)


Документация для CrossOriginFilter:

http://www.eclipse.org/jetty/documentation/current/cross-origin-filter.html

Javadoc для CrossOriginFilter:

http://download.eclipse.org/jetty/stable-9/apidocs/org/eclipse/jetty/servlets/CrossOriginFilter.html

Фактический исходный код: (иногда это тоже помогает людям понять):

https://github.com/eclipse/jetty.project/blob/jetty-9.2.3.v20140905/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/CrossOriginFilter.java

Короче говоря, вы, вероятно, захотите добавить OPTIONS к разрешенным методам.

(Как написано в javadoc)

FilterHolder holder = new FilterHolder(new CrossOriginFilter());
holder.setInitParameter(CrossOriginFilter.ALLOWED_METHODS_PARAM, "GET,POST,HEAD,OPTIONS");
appHandler.addFilter(holder, "/*", EnumSet.of(DispatcherType.REQUEST));

Теперь, чтобы устранить еще одну ошибку, которую вы имеете...

Это ничего не делает...

holder.setInitParameter(CrossOriginFilter.ACCESS_CONTROL_ALLOW_CREDENTIALS_HEADER,
  "true");

Это не ключ параметра инициализации. (На самом деле это константа имени заголовка для Access-Control-Allow-Credentials), если вы хотите разрешить учетные данные, сделайте так, как говорит javadoc.

holder.setInitParameter(CrossOriginFilter.ALLOW_CREDENTIALS_PARAM, "true");
person Joakim Erdfelt    schedule 24.09.2014
comment
Спасибо - я нашел это, но я действительно до сих пор не знаю, почему я получаю 401 в ответ на запрос OPTIONS. - person Jer; 24.09.2014
comment
Я не уверен, что технически запрос OPTIONS подпадает под действие CORS, но запрос OPTIONS, конечно, по-прежнему поступает из другого домена (поскольку POST выполняется из другого домена). Я не уверен, что должен показывать этот код. Это просто говорит о том, что для того, чтобы быть предварительным запросом, метод должен быть OPTIONS. Возможно, я что-то упускаю, но ничего не отклоняется — во всяком случае, все, кроме OPTIONS, отклоняется. - person Jer; 25.09.2014
comment
Вы можете использовать CrossOriginFilter.ALLOWED_METHODS_PARAM вместо allowedMethods - person daka; 11.04.2018

Я решил эту проблему, используя следующую конфигурацию для FilterHolder:

FilterHolder cors = new FilterHolder(CrossOriginFilter.class);
cors.setInitParameter(CrossOriginFilter.ALLOWED_ORIGINS_PARAM, "*");
cors.setInitParameter(CrossOriginFilter.ACCESS_CONTROL_ALLOW_ORIGIN_HEADER, "*");
cors.setInitParameter(CrossOriginFilter.ALLOWED_METHODS_PARAM, "OPTIONS,GET,POST,HEAD");
cors.setInitParameter(CrossOriginFilter.ALLOWED_HEADERS_PARAM, "X-Requested-With,Content-Type,Accept,Origin,Cache-Control");
cors.setInitParameter(CrossOriginFilter.CHAIN_PREFLIGHT_PARAM, "false");

Chrome отправляет заголовок «Cache-Control». Если вы не разрешите этот заголовок с помощью своего фильтра CORS, то на запрос OPTIONS не будет дан ответ с правильными заголовками. Большинство примеров CrossOriginFilter онлайн не включают этот заголовок.

При желании вы можете установить CHAIN_PREFLIGHT_PARAM на false (по умолчанию true). Если вы установите его на false, фильтр ответит на запрос, не отправляя запрос сервлету. Если вы хотите обработать запрос OPTIONS самостоятельно, вам не нужно устанавливать этот параметр.

person modelDBA    schedule 24.09.2017

Обновление: я точно попробовал ваш код, но добавил фильтр в обработчик контекста, а не в приложение. Это работает следующим образом.

        FilterHolder holder = new FilterHolder(new CrossOriginFilter());
    holder.setInitParameter(CrossOriginFilter.ALLOWED_ORIGINS_PARAM,  "http://localhost:8100");
    holder.setInitParameter(CrossOriginFilter.ACCESS_CONTROL_ALLOW_CREDENTIALS_HEADER, "true");

    contextHandler.addFilter(holder, "/*", EnumSet.of(DispatcherType.REQUEST)); 
person Abdo    schedule 25.09.2014
comment
Извините, я не включил достаточно кода. appHandler является ServletContextHandler, кажется: ServletContextHandler appHandler = new ServletContextHandler(ServletContextHandler.SESSIONS); - person Jer; 25.09.2014