Spring - перенаправление на ссылку после успешного входа в систему после неудачного запроса Ajax - PullRequest
0 голосов
/ 27 октября 2018

У меня есть веб-сайт, который требует, чтобы некоторый HTML отображался внутри элемента асинхронно после действий пользователя. Если время сеанса пользователя истекает, все становится сложно, но его можно решить, создав собственный класс AuthenticationEntryPoint, такой как этот вопрос SO и этот вопрос SO предложить.

Моя проблема возникает, когда пользователь снова входит в систему, потому что пользователь перенаправляется на последний запрошенный URL-адрес, который является запросом Ajax, поэтому мой пользователь перенаправляется на фрагмент HTML, а не на последнюю страницу это просматривают.

Мне удалось решить эту проблему, удалив атрибут сеанса в пользовательском AuthenticationEntryPoint:

if (ajaxOrAsync) {
    request.getSession().removeAttribute("SPRING_SECURITY_SAVED_REQUEST");
}

Вот проблема моего вопроса.

Хотя предыдущий код решает мою проблему, он имеет побочный эффект перенаправления пользователя на домашнюю страницу вместо последней просмотренной страницы (так как нет сохраненного запроса). Это не будет большой проблемой, но это делает сайт несовместимым, потому что, если последний запрос был асинхронным, он перенаправляется домой, но если это был обычный запрос, он перенаправляется на последнюю просмотренную страницу. = (* 1 018 *

Мне удалось написать код для обработки этого сценария:

import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.PortResolver;
import org.springframework.security.web.PortResolverImpl;
import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint;
import org.springframework.security.web.savedrequest.DefaultSavedRequest;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;

import static javax.servlet.http.HttpServletResponse.SC_UNAUTHORIZED;
import static org.apache.commons.lang.StringUtils.isBlank;

public class CustomAuthenticationEntryPoint extends LoginUrlAuthenticationEntryPoint {
    ... // Some not so relevant code

    @Override
    public void commence(final HttpServletRequest request,
                         final HttpServletResponse response,
                         final AuthenticationException authException) throws IOException, ServletException {
        ... // some code to determine if the request is an ajax request or an async one
        if (ajaxOrAsync) {
            useRefererAsSavedRequest(request);

            response.sendError(SC_UNAUTHORIZED);
        } else {
            super.commence(request, response, authException);
        }
    }

    private void useRefererAsSavedRequest(final HttpServletRequest request) {
        request.getSession().removeAttribute(SAVED_REQUEST_SESSION_ATTRIBUTE);

        final URL refererUrl = getRefererUrl(request);
        if (refererUrl != null) {
            final HttpServletRequestWrapper newRequest = new CustomHttpServletRequest(request, refererUrl);
            final PortResolver portResolver = new PortResolverImpl();
            final DefaultSavedRequest newSpringSecuritySavedRequest = new DefaultSavedRequest(newRequest, portResolver);

            request.getSession().setAttribute(SAVED_REQUEST_SESSION_ATTRIBUTE, newSpringSecuritySavedRequest);
        }
    }

    private URL getRefererUrl(final HttpServletRequest request) {
        final String referer = request.getHeader("referer");

        if (isBlank(referer)) {
            return null;
        }

        try {
            return new URL(referer);
        } catch (final MalformedURLException exception) {
            return null;
        }
    }

    private class CustomHttpServletRequest extends HttpServletRequestWrapper {
        private URL url;

        public CustomHttpServletRequest(final HttpServletRequest request, final URL url) {
            super(request);
            this.url = url;
        }

        @Override
        public String getRequestURI() {
            return url.getPath();
        }

        @Override
        public StringBuffer getRequestURL() {
            return new StringBuffer(url.toString());
        }

        @Override
        public String getServletPath() {
            return url.getPath();
        }

    }
}

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

Итак, мой вопрос: Есть ли другой способ переписать ссылку, которую Spring использует для перенаправления пользователя после успешного входа в систему (учитывая условия, с которыми я работаю)?

Я посмотрел на AuthenticationSuccessHandler от Spring , но не нашел способа передать ему URL-адрес реферера в случае неудачного запроса Ajax.

1 Ответ

0 голосов
/ 30 октября 2018

Я нашел приемлемое решение моей проблемы благодаря идее, которая возникла, когда читал документы , а затем просматривал этот другой SO ответ .Короче говоря, мне пришлось бы создать свой собственный ExceptionTranslationFilter и переопределить sendStartAuthentication, чтобы не сохранять кэш запросов.

Если взглянуть на код ExceptionTranslationFilter, он выглядит так(для Finchley SR1):

protected void sendStartAuthentication(HttpServletRequest request,
        HttpServletResponse response, FilterChain chain,
        AuthenticationException reason) throws ServletException, IOException {
    SecurityContextHolder.getContext().setAuthentication(null);
    requestCache.saveRequest(request, response); // <--- Look at me
    logger.debug("Calling Authentication entry point.");
    authenticationEntryPoint.commence(request, response, reason);
}

Итак, чтобы не сохранять данные из запросов Ajax, я должен реализовать CustomExceptionTranslationFilter, который действует следующим образом:

@Override
protected void sendStartAuthentication(final HttpServletRequest request,
                                       final HttpServletResponse response,
                                       final FilterChain chain,
                                       final AuthenticationException authenticationException) throws ServletException, IOException {
    ... // some code to determine if the request is an ajax request or an async one
    if (isAjaxOrAsyncRequest) {
        SecurityContextHolder.getContext().setAuthentication(null);

        authenticationEntryPoint.commence(request, response, authenticationException);
    } else {
        super.sendStartAuthentication(request, response, chain, authenticationException);
    }
}

Это делает CustomAuthenticationEntryPoint логика намного проще:

@Override
public void commence(final HttpServletRequest request,
                     final HttpServletResponse response,
                     final AuthenticationException authException) throws IOException, ServletException {
    ... // some code to determine if the request is an ajax request or an async one, again
    if (isAjaxOrAsyncRequest) {
        response.sendError(SC_UNAUTHORIZED);
    } else {
        super.commence(request, response, authException);
    }
}

И мой CustomWebSecurityConfigurerAdapter должен быть настроен так:

@Override
protected void configure(final HttpSecurity http) throws Exception {
    final CustomAuthenticationEntryPoint customAuthenticationEntryPoint =
            new CustomAuthenticationEntryPoint("/login-path");

    final CustomExceptionTranslationFilter customExceptionTranslationFilter =
            new CustomExceptionTranslationFilter(customAuthenticationEntryPoint);

    http.addFilterAfter(customExceptionTranslationFilter, ExceptionTranslationFilter.class)
            ....
            .permitAll()
            .anyRequest().authenticated()
            .and()
            .formLogin()
            .and()
            .exceptionHandling()
            .authenticationEntryPoint(customAuthenticationEntryPoint)
            ....;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...