Рубрики
Без рубрики

Spring Security – Перенаправление на предыдущий URL-адрес после входа в систему

Краткий пример перенаправления после входа в систему в Spring Security

Автор оригинала: baeldung.

1. Обзор

Эта статья будет посвящена тому, как перенаправить пользователя обратно на первоначально запрошенный URL – адрес-после входа в систему .

Ранее мы видели, как перенаправлять на разные страницы после входа в систему с помощью Spring Security для разных типов пользователей, и рассматривали различные типы перенаправлений с помощью Spring MVC .

Эта статья основана на учебнике по входу в систему Spring Security.

2. Обычная практика

Наиболее распространенными способами реализации логики перенаправления после входа в систему являются:

  • использование HTTP-реферера заголовка
  • сохранение исходного запроса в сеансе
  • добавление исходного URL-адреса к перенаправленному URL-адресу входа в систему

Использование HTTP Referer заголовка является простым способом для большинства браузеров и HTTP клиентов автоматически устанавливать Referer . Однако, поскольку Referer поддается подделке и зависит от реализации клиента, использование заголовка HTTP Referer для реализации перенаправления обычно не рекомендуется.

Сохранение исходного запроса в сеансе является безопасным и надежным способом реализации такого рода перенаправления. Помимо исходного URL-адреса, мы можем хранить исходные атрибуты запроса и любые пользовательские свойства в сеансе.

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

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

В весенней безопасности первые два подхода изначально поддерживаются.

3. AuthenticationSuccessHandler

При аутентификации на основе форм перенаправление происходит сразу после входа в систему, который обрабатывается в экземпляре AuthenticationSuccessHandler в Spring Security .

Предусмотрены три реализации по умолчанию: SimpleUrlAuthenticationSuccessHandler , SavedRequestAwareAuthenticationSuccessHandler и ForwardAuthenticationSuccessHandler . Мы сосредоточимся на первых двух реализациях.

3.1. SavedRequestAwareAuthenticationSuccessHandler

SavedRequestAwareAuthenticationSuccessHandler использует сохраненный запрос, хранящийся в сеансе. После успешного входа в систему пользователи будут перенаправлены на URL-адрес, сохраненный в исходном запросе.

Для входа в форму SavedRequestAwareAuthenticationSuccessHandler используется по умолчанию AuthenticationSuccessHandler .

@Configuration
@EnableWebSecurity
public class RedirectionSecurityConfig extends WebSecurityConfigurerAdapter {

    //...

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
          .authorizeRequests()
          .antMatchers("/login*")
          .permitAll()
          .anyRequest()
          .authenticated()
          .and()
          .formLogin();
    }
    
}

И эквивалентный XML будет:


    
    
    

Предположим, у нас есть защищенный ресурс в местоположении “/защищенный”. При первом доступе к ресурсу мы будем перенаправлены на страницу входа в систему; после заполнения учетных данных и публикации формы входа в систему мы будем перенаправлены обратно в первоначально запрошенное местоположение ресурса:

@Test
public void givenAccessSecuredResource_whenAuthenticated_thenRedirectedBack() 
  throws Exception {
 
    MockHttpServletRequestBuilder securedResourceAccess = get("/secured");
    MvcResult unauthenticatedResult = mvc
      .perform(securedResourceAccess)
      .andExpect(status().is3xxRedirection())
      .andReturn();

    MockHttpSession session = (MockHttpSession) unauthenticatedResult
      .getRequest()
      .getSession();
    String loginUrl = unauthenticatedResult
      .getResponse()
      .getRedirectedUrl();
    mvc
      .perform(post(loginUrl)
        .param("username", userDetails.getUsername())
        .param("password", userDetails.getPassword())
        .session(session)
        .with(csrf()))
      .andExpect(status().is3xxRedirection())
      .andExpect(redirectedUrlPattern("**/secured"))
      .andReturn();

    mvc
      .perform(securedResourceAccess.session(session))
      .andExpect(status().isOk());
}

3.2. SimpleUrlAuthenticationSuccessHandler

По сравнению с SavedRequestAwareAuthenticationSuccessHandler , SimpleUrlAuthenticationSuccessHandler дает нам больше возможностей для принятия решений о перенаправлении.

Мы можем включить перенаправление на основе ссылок с помощью set User Referer(true) :

public class RefererRedirectionAuthenticationSuccessHandler 
  extends SimpleUrlAuthenticationSuccessHandler
  implements AuthenticationSuccessHandler {

    public RefererRedirectionAuthenticationSuccessHandler() {
        super();
        setUseReferer(true);
    }

}

Затем используйте его в качестве AuthenticationSuccessHandler в Конфигурации безопасности перенаправления :

@Override
protected void configure(HttpSecurity http) throws Exception {
    http
      .authorizeRequests()
      .antMatchers("/login*")
      .permitAll()
      .anyRequest()
      .authenticated()
      .and()
      .formLogin()
      .successHandler(new RefererAuthenticationSuccessHandler());
}

И для конфигурации XML:


    
    
    


3.3. Под капотом

В этих простых в использовании функциях Spring Security нет никакой магии . Когда запрашивается защищенный ресурс, запрос будет отфильтрован цепочкой различных фильтров. Будут проверены участники проверки подлинности и разрешения. Если сеанс запроса еще не аутентифицирован, будет выдано исключение AuthenticationException .

Исключение AuthenticationException будет поймано в фильтре ExceptionTranslationFilter , в котором будет запущен процесс аутентификации, что приведет к перенаправлению на страницу входа в систему.

public class ExceptionTranslationFilter extends GenericFilterBean {

    //...

    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
      throws IOException, ServletException {
        //...

        handleSpringSecurityException(request, response, chain, ase);

        //...
    }

    private void handleSpringSecurityException(HttpServletRequest request,
      HttpServletResponse response, FilterChain chain, RuntimeException exception)
      throws IOException, ServletException {

        if (exception instanceof AuthenticationException) {

            sendStartAuthentication(request, response, chain,
              (AuthenticationException) exception);

        }

        //...
    }

    protected void sendStartAuthentication(HttpServletRequest request,
      HttpServletResponse response, FilterChain chain,
      AuthenticationException reason) throws ServletException, IOException {
       
       SecurityContextHolder.getContext().setAuthentication(null);
       requestCache.saveRequest(request, response);
       authenticationEntryPoint.commence(request, response, reason);
    }

    //... 

}

После входа в систему мы можем настроить поведение в AuthenticationSuccessHandler , как показано выше.

4. Заключение

В этом примере Spring Security мы обсудили распространенную практику перенаправления после входа в систему и объяснили реализации с использованием Spring Security.

Обратите внимание, что все реализации, о которых мы упоминали, уязвимы для определенных атак, если не применяется проверка или дополнительные элементы управления методами . В результате таких атак пользователи могут быть перенаправлены на вредоносный сайт.

OWASP предоставил шпаргалку , чтобы помочь нам обрабатывать непроверенные перенаправления и переадресации. Это очень помогло бы, если бы нам нужно было создавать реализации самостоятельно.

Полный код реализации этой статьи можно найти на Github .