Once you have protected your Spring website against CSRF using the Synchronizer Token Pattern you will find that the redirection to the requested page after login functionality (You request a page, the login form shows up and after that you are taken to the page you originally requested) will be broken.
Basically the user might access a bookmark or just type a URL without the security token and the redirection will use the provided (and expired) security token or no security token at all. Of course you need to hook into Spring in order to change the default functionality.
First you use "authentication-success-handler-ref" form-login property in the Spring security context:
<beans:bean id="customAuthenticationHandler" class="com.nestorurquiza.web.handler.CustomAuthenticationHandler" /> <form-login login-page="/login" authentication-success-handler-ref="customAuthenticationHandler" authentication-failure-url="/login?error=authorizationFailed" />
Then implement the custom handler. Code should speak for itself.
package com.nestorurquiza.web.handler; import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.security.core.Authentication; import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler; import org.springframework.security.web.savedrequest.DefaultSavedRequest; import com.nestorurquiza.utils.UrlTool; import com.nestorurquiza.web.WebConstants; public class CustomAuthenticationHandler extends SavedRequestAwareAuthenticationSuccessHandler { @Override public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws ServletException, IOException { // TODO Auto-generated method stub String ctoken = (String) request.getSession().getAttribute(WebConstants.CSRF_TOKEN); DefaultSavedRequest defaultSavedRequest = (DefaultSavedRequest) request.getSession().getAttribute("SPRING_SECURITY_SAVED_REQUEST_KEY"); if( defaultSavedRequest != null && ctoken != null ) { String requestUrl = defaultSavedRequest.getRequestURL() + "?" + defaultSavedRequest.getQueryString(); requestUrl = UrlTool.addParamToURL(requestUrl, WebConstants.CSRF_TOKEN, ctoken, true); getRedirectStrategy().sendRedirect(request, response, requestUrl); } else { super.onAuthenticationSuccess(request, response, authentication); } } }
Here is the little useful class that allows to override the ctoken parameter (or any other url parameter)
package com.nestorurquiza.utils; public class UrlTool { public static String addParamToURL(String url, String param, String value, boolean replace) { if (replace == true) url = removeParamFromURL(url, param); return url + ((url.indexOf("?") == -1) ? "?" : "&") + param + "=" + value; } public static String removeParamFromURL(String url, String param) { String sep = "&"; int startIndex = url.indexOf(sep + param + "="); boolean firstParam = false; if (startIndex == -1) { startIndex = url.indexOf("?" + param + "="); if (startIndex != -1) { startIndex++; firstParam = true; } } if (startIndex != -1) { String startUrl = url.substring(0, startIndex); String endUrl = ""; int endIndex = url.indexOf(sep, startIndex + 1); if(firstParam && endIndex != 1) { //remove separator from remaining url endUrl = url.substring(endIndex + 1); } return startUrl + endUrl; } return url; } }