There is a good convenience to user’s usability, allowing to return to requested, unreacheable resource (before login), when the authenticaton has been processed properly.
Spring Security allows to define return URL after login by using SimpleUrlAuthenticationSuccessHandler
. To configure this feature, you need to define the bean in security context:
<beans:bean id="simpleUrlAuthenticationSuccessHandler" class="org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler">
<beans:property name="defaultTargetUrl" value="/account"/>
<beans:property name="targetUrlParameter" value="spring-security-redirect"/>
</beans:bean>
<http ...>
<form-login ... authentication-success-handler-ref="simpleUrlAuthenticationSuccessHandler" />
</http>
Now, if user log in successfully, then will be redirected to the URL passed into spring-security-redirect
login form parameter (e.x. in hidden field).
NOTE: if you are using own form template, don’t forget about generate the hidden field, for e.x. get r attribute from URL:
<input type="hidden" name="spring-security-redirect" value="<c:out value="${param.r}" />">
NOTE2: Use c:out value="${param.r}"
instead of simple ${param.r}
, because c:out
prevent to inject executable code [read more].
The tag can automatically escape XML tags so they aren’t evaluated as actual tags.
How to redirect user when it is not signed-in?
The example above provides us the way of redirecting user, when access the login form. We have passed the parameter r to the form, but it is still not provided to the login form page in query string.
Even user access the protected
page, the ExceptionTranslationFilter
is executed. There are two reasons, when we looking for resolve this problem:
- When user is not logged in, so don’t have any roles, so don’t have permission to show a resource. Then
AuthenticationEntryPoint
should be executed.UX: User should be redirected to the login form to allow obtain the credentials. - The second, when user is logged in and don’t have role (credential) that allow to access the protected resource. Then
AccessDeniedHandler
should is executed.UX: The403
(Access Denied) page should be displayed. In nice form, obviously.
Now we will consider the first case, when user is trying to access protected resource, will be redirected to the login page, and then, should be redirected back to the resource to consider second case.
Define custom AuthenticationEntryPoint
implementation to return user to the login page, with defined parameter r
filled as current page:
@Component(value = "customAuthenticationEntryPoint")
public class CustomAuthenticationEntryPoint implements AuthenticationEntryPoint {
private String loginPageUrl;
private boolean returnParameterEnabled;
private String returnParameterName;
@Override
public void commence(HttpServletRequest request, HttpServletResponse response,
AuthenticationException reason) throws IOException, ServletException {
if(null == loginPageUrl || "".equals(loginPageUrl))
throw new RuntimeException("loginPageUrl has not been defined");
String redirectUrl = loginPageUrl;
if(isReturnParameterEnabled()) {
String redirectUrlReturnParameterName = getReturnParameterName();
if(null == redirectUrlReturnParameterName || "".equals(redirectUrlReturnParameterName))
throw new RuntimeException("redirectUrlReturnParameterName has not been defined");
redirectUrl += "?" + redirectUrlReturnParameterName + "=" + request.getServletPath();
}
RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();
redirectStrategy.sendRedirect(request, response, redirectUrl);
return;
}
public String getLoginPageUrl() {
return loginPageUrl;
}
public void setLoginPageUrl(String loginPageUrl) {
this.loginPageUrl = loginPageUrl;
}
public boolean isReturnParameterEnabled() {
return returnParameterEnabled;
}
public void setReturnParameterEnabled(boolean returnParameterEnabled) {
this.returnParameterEnabled = returnParameterEnabled;
}
public String getReturnParameterName() {
return returnParameterName;
}
public void setReturnParameterName(String returnParameterName) {
this.returnParameterName = returnParameterName;
}
}
And configure this bean with tag:
<http ... entry-point-ref="customAuthenticationEntryPoint">
<form-login login-page="/login" authentication-failure-url="/login?authfailed=1" authentication-success-handler-ref="simpleUrlAuthenticationSuccessHandler" />
</http>
<beans:bean id="customAuthenticationEntryPoint" class="pl.athlan.util.CustomAuthenticationEntryPoint">
<beans:property name="loginPageUrl" value="/login" />
<beans:property name="returnParameterEnabled" value="true" />
<beans:property name="returnParameterName" value="r" />
</beans:bean>
<beans:bean id="simpleUrlAuthenticationSuccessHandler" class="org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler">
<beans:property name="defaultTargetUrl" value="/account"/>
<beans:property name="targetUrlParameter" value="spring-security-redirect"/>
</beans:bean>
That’s all. Hope will help.
TIP: to save your time, don’t try to fight with configuration of ExceptionTranslationFilter
. I have tried to override org.springframework.security.web.access.ExceptionTranslationFilter, without effects:
<beans:bean id="exceptionTranslationFilter" class="org.springframework.security.web.access.ExceptionTranslationFilter">
<beans:property name="authenticationEntryPoint" ref="customAuthenticationEntryPoint"/>
<beans:property name="accessDeniedHandler" ref="accessDeniedHandler"/>
</beans:bean>