Spring Security - Redirecionar para o URL anterior após o login
1. Visão geral
Este artigo se concentrará emhow to redirect a user back to the originally requested URL – after they log in.
Anteriormente, vimoshow to redirect to different pages after login with Spring Security para diferentes tipos de usuários e cobrimos vários tipos deredirections with Spring MVC.
O artigo é baseado no tutorialSpring Security Login.
2. Prática comum
As maneiras mais comuns de implementar a lógica de redirecionamento após o login são:
-
usando o cabeçalhoHTTP Referer
-
salvando a solicitação original na sessão
-
anexando URL original ao URL de login redirecionado
Using the HTTP Referer header é uma maneira direta, para a maioria dos navegadores e clientesHTTP definirReferer automaticamente. No entanto, comoReferer é forjado e depende da implementação do cliente, o uso do cabeçalhoHTTP Referer para implementar o redirecionamento geralmente não é sugerido.
Saving the original request in the session é uma maneira segura e robusta de implementar esse tipo de redirecionamento. Além da URL original, podemos armazenar atributos de solicitação originais e quaisquer propriedades personalizadas na sessão.
And appending original URL to the redirected login URL geralmente é visto em implementações de SSO. Quando autenticados por meio de um serviço SSO, os usuários serão redirecionados para a página solicitada originalmente, com o URL anexado. Devemos garantir que o URL anexado seja codificado corretamente.
Outra implementação semelhante é colocar o URL da solicitação original em um campo oculto dentro do formulário de login. Mas isso não é melhor do que usarHTTP Referer
No Spring Security, as duas primeiras abordagens têm suporte nativo.
3. AuthenticationSuccessHandler
Na autenticação baseada em formulário, o redirecionamento ocorre logo após o login, que é tratado em uma instânciaAuthenticationSuccessHandler emSpring Security.
Três implementações padrão são fornecidas:SimpleUrlAuthenticationSuccessHandler,SavedRequestAwareAuthenticationSuccessHandlereForwardAuthenticationSuccessHandler. Vamos nos concentrar nas duas primeiras implementações.
3.1. SavedRequestAwareAuthenticationSuccessHandler
SavedRequestAwareAuthenticationSuccessHandler faz uso da solicitação salva armazenada na sessão. Após um login bem-sucedido, os usuários serão redirecionados para o URL salvo na solicitação original.
Para login de formulário,SavedRequestAwareAuthenticationSuccessHandler é usado comoAuthenticationSuccessHandler padrão.
@Configuration
@EnableWebSecurity
public class RedirectionSecurityConfig extends WebSecurityConfigurerAdapter {
//...
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/login*")
.permitAll()
.anyRequest()
.authenticated()
.and()
.formLogin();
}
}
E o XML equivalente seria:
Suponha que tenhamos um recurso protegido no local "/ protegido". Para o primeiro acesso ao recurso, seremos redirecionados para a página de login; depois de preencher as credenciais e postar o formulário de login, seremos redirecionados para o local do recurso originalmente solicitado:
@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
Em comparação comSavedRequestAwareAuthenticationSuccessHandler,SimpleUrlAuthenticationSuccessHandler nos dá mais opções nas decisões de redirecionamento.
Podemos habilitar o redirecionamento baseado em Referer porsetUserReferer(true):
public class RefererRedirectionAuthenticationSuccessHandler
extends SimpleUrlAuthenticationSuccessHandler
implements AuthenticationSuccessHandler {
public RefererRedirectionAuthenticationSuccessHandler() {
super();
setUseReferer(true);
}
}
Em seguida, use-o comoAuthenticationSuccessHandler emRedirectionSecurityConfig:
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/login*")
.permitAll()
.anyRequest()
.authenticated()
.and()
.formLogin()
.successHandler(new RefererAuthenticationSuccessHandler());
}
E para configuração XML:
3.3. Sob o capô
Não há mágica nesses recursos fáceis de usar emSpring Security. Quando um recurso protegido está sendo solicitado, a solicitação será filtrada por uma cadeia de vários filtros. Os princípios e permissões de autenticação serão verificados. Se a sessão de solicitação ainda não foi autenticada,AuthenticationException será lançado.
OAuthenticationException será capturado emExceptionTranslationFilter, em que um processo de autenticação será iniciado, resultando em um redirecionamento para a página de login.
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);
}
//...
}
Após o login, podemos personalizar os comportamentos em umAuthenticationSuccessHandler, conforme mostrado acima.
4. Conclusão
Neste exemploSpring Security, discutimos a prática comum para redirecionamento após o login e explicamos as implementações usando Spring Security.
Observe queall the implementations we mentioned are vulnerable to certain attacks if no validation or extra method controls are applied. Os usuários podem ser redirecionados para um site malicioso por esses ataques.
OOWASP forneceu umcheat sheet para nos ajudar a lidar com redirecionamentos e encaminhamentos não validados. Isso ajudaria bastante se precisarmos criar implementações por conta própria.
O código de implementação completo deste artigo pode ser encontradoover on Github.