Добавьте заголовки CORS в ответ j_security_check

Я создаю REST API с помощью jax-rs и WildFly 10. Некоторые конечные точки защищены. Я использую аутентификацию на основе FORM.

В моем коде javascript я проверяю ответ на запрос AJAX, и если он установлен на 401 Unauthorized, я затем предоставляю пользователю форму входа. Когда он заполняет его, я отправляю детали j_security_check.

Работая на localhost, все это работает нормально, но когда веб-сервер и сервер REST находятся на разных машинах, браузер отклоняет запрос AJAX из-за проблем с перекрестным происхождением.

Я понимаю CORS, поэтому я добавил фильтр CORS на свой сервер REST, который устанавливает заголовки CORS для сервера с графическим интерфейсом. Все работает отлично, за исключением одной небольшой, но важной детали: после успешного входа CORS-фильтр не срабатывает для ответа j_security_check. Заголовки CORS не добавляются, и браузер не может прочитать ответ.

Помимо этой детали, у меня вся установка работает именно так, как я хочу... Но я всю ночь боролся с этой деталью и никак не могу заставить ее работать.

Я понимаю, что есть проблемы с попыткой отфильтровать j_security_check, но я не знаю других способов добавить заголовки CORS... Итак, мой вопрос:

Как добавить заголовки CORS в ответ для j_security_check?


person Stijn de Witt    schedule 05.02.2016    source источник
comment
Привет! Я столкнулся с точно такой же проблемой при реализации ContainerResponseFilter. Я могу добавить все необходимые заголовки Access-Control-Allow-XXX к любому ответу на стороне сервера. Но ответ на запрос j_security_check является исключением. Вам удалось найти решение? Заранее спасибо:).   -  person StepUp    schedule 26.08.2016
comment
Да... вроде. Не очень доволен этим, но вроде работает. Я опубликую ответ.   -  person Stijn de Witt    schedule 28.08.2016


Ответы (3)


Настройка подсистемы undertow в файле standalone.xml/domain.xml решила эту проблему для меня. Настроенные там фильтры обрабатывают все запросы, включая j_security_check один.

<subsystem xmlns="urn:jboss:domain:undertow:3.0">
            <buffer-cache name="default"/>
            <server name="default-server">
                <http-listener name="default" redirect-socket="https" socket-binding="http"/>
                <host name="default-host" alias="localhost">
                    <location name="/" handler="welcome-content"/>
                    <filter-ref name="server-header"/>
                    <filter-ref name="x-powered-by-header"/>
                    <!--CORS headers -->
                    <filter-ref name="Access-Control-Allow-Origin"/>
                    <filter-ref name="Access-Control-Allow-Methods"/>
                    <filter-ref name="Access-Control-Allow-Headers"/>
                    <filter-ref name="Access-Control-Allow-Credentials"/>
                    <filter-ref name="Access-Control-Max-Age"/>
                </host>
            </server>
            <servlet-container name="default">
                <jsp-config/>
                <websockets/>
            </servlet-container>
            <handlers>
                <file name="welcome-content" path="${jboss.home.dir}/welcome-content"/>
            </handlers>
            <filters>
                <response-header name="server-header" header-value="WildFly/10" header-name="Server"/>
                <response-header name="x-powered-by-header" header-value="Undertow/1" header-name="X-Powered-By"/>
                <!-- CORS headers -->
                <response-header name="Access-Control-Allow-Origin" header-name="Access-Control-Allow-Origin" header-value="*"/>
                <response-header name="Access-Control-Allow-Methods" header-name="Access-Control-Allow-Methods" header-value="OPTIONS, GET, POST, PUT, DELETE"/>
                <response-header name="Access-Control-Allow-Headers" header-name="Access-Control-Allow-Headers" header-value="accept, authorization, content-type, x-requested-with"/>
                <response-header name="Access-Control-Allow-Credentials" header-name="Access-Control-Allow-Credentials" header-value="true"/>
                <response-header name="Access-Control-Max-Age" header-name="Access-Control-Max-Age" header-value="60"/>
            </filters>
        </subsystem>

Конечно, вам лучше заменить подстановочный знак "*" URL-адресом вашего GUI-сервера в атрибуте value заголовка Access-Control-Allow-Origin.

person nfedorov    schedule 28.08.2016
comment
Какое отличное решение :) - person StepUp; 28.08.2016
comment
Спасибо за вашу помощь. К сожалению, несколько доменов не допускаются в качестве исходного значения cors. Как и *. И вы не можете иметь несколько заголовков происхождения cors в одном ответе... Это фактически означает, что вы не можете использовать статическую конфигурацию, подобную этой, если у вас есть несколько доменов (поскольку большинство сайтов поддерживают как URL-адреса открытого домена, так и те, которые начинаются с www .) - person Stijn de Witt; 26.04.2017
comment
@nfedorov Ну, это полтора года спустя, и за это время я провел много исследований по этому вопросу, и кажется, что вы правы на 100%. Способ, который вы описываете здесь с фильтром Undertow, кажется единственным способом отфильтровать эти запросы. Фильтры сервлетов и фильтры JAX-RS не вызываются для этих запросов. - person Stijn de Witt; 03.02.2018
comment
Чтобы решить проблему со значением для Access-Control-Allow-Origin, которое должно быть динамическим, я создал фильтр Undertow специально для добавления заголовков CORS. Я добавлю ответ об этом. - person Stijn de Witt; 03.02.2018
comment
@nfedorov, я все еще получаю Причина: предварительная проверка канала CORS не удалась. Что мне нужно изменить, чтобы заставить его работать? - person gouessej; 03.07.2020

Выполняйте вход самостоятельно.

Вместо публикации на j_security_check отправьте сообщение на /auth/login или что-то в этом роде и обработайте логин. Что-то вроде этого:

@POST
@Path("login")
@PermitAll
public Response postLogin() {
    String user = request.getParameter("j_username");
    String password = request.getParameter("j_password");
    StringBuffer buf = request.getRequestURL();
    URI redir = null; 
    try {redir = new URI(buf.substring(0, buf.lastIndexOf("/login")) + "/session");} 
    catch (URISyntaxException e) {}
    try {
        request.login(user, password);
        return Response.seeOther(redir).build();
    } catch (ServletException e) {
        if (e.getMessage() != null && e.getMessage().equals("UT010030: User already logged in")) {
            Response.seeOther(redir).build();
        }
        return Response.status(Status.FORBIDDEN).build();
    }
}
person Stijn de Witt    schedule 27.08.2016
comment
Я не принимаю этот ответ, потому что на самом деле это обходной путь... опять же, это может быть единственный способ. Кто знает? Все еще надеюсь на дополнительную информацию от сообщества SO. - person Stijn de Witt; 28.08.2016
comment
undertow-cors-filter, вероятно, лучший способ справиться с этим банкоматом. - person Stijn de Witt; 31.07.2019

На основе ответа nfedorov , я пришел к выводу, что он прав и что фильтр Undertow — единственный тип фильтра, который может работать с ответами контейнера. Поэтому, если мы хотим добавить к этим ответам заголовки CORS, нам нужен фильтр Undertow. Итак, я написал один:

undertow-cors-filter

Загрузите ZIP-файл и разархивируйте его в корневую папку Wildfly. Затем добавьте конфигурацию filter в standalone.xml:

<filters>
  <filter name="undertow-cors-filter" class-name="com.stijndewitt.undertow.cors.Filter" module="com.stijndewitt.undertow.cors">

  </filter>
</filters>

Добавьте filter-ref к элементу host (все еще в standalone.xml):

<host name="default-host" alias="localhost">
  <filter-ref name="undertow-cors-filter" />
</host>

Это добавит заголовки CORS, разрешающие всем источникам доступ ко всем ответам, которые выдает этот сервер.

Добавьте params к определению filter, чтобы настроить поведение. Например, если вы хотите, чтобы фильтр применялся только к ответам на запросы к URL-адресам, начинающимся с /api, и вы хотите разрешить доступ только к example.com и example.org, вы можете использовать конфигурацию, подобную этой:

<filters>
  <filter name="undertow-cors-filter" class-name="com.stijndewitt.undertow.cors.Filter" module="com.stijndewitt.undertow.cors">
    <param name="urlPattern" value="^http(s)?://([^/]+)(:([^/]+))?(/([^/])+)?/api(/.*)?$" />
    <param name="policyClass" value="com.stijndewitt.undertow.cors.AllowMatching" />
    <param name="policyParam" value="^http(s)?://(www\.)?example\.(com|org)$" />   
  </filter>
</filters>

Доступны 3 класса политик: AllowAll, AllowMatching и Whitelist, и при необходимости вы можете написать свои собственные политики.

person Stijn de Witt    schedule 03.02.2018