On login failure I want to redirect the user to an error page and display a meaningful error message. Is it possible to add Flash Attributes that will be passed to the subsequent request?
The code presented below doesn't work. RequestContextUtils.getOutputFlashMap()
returns null
.
public class CustomAuthenticationFailureHandler implements AuthenticationFailureHandler
{
@Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException
{
FlashMap outputFlashMap = RequestContextUtils.getOutputFlashMap(request);
if (outputFlashMap != null)
{
outputFlashMap.put("error", "Error message");
}
response.sendRedirect(request.getContextPath() + "/error");
}
}
I encountered this same problem with Spring 4.3.17 and finally found a solution by stepping through the spring-webmvc code and making educated guesses about how to integrate Flash Attributes outside the normal framework. SessionFlashMapManager is the key to getting this to work. I believe this method should work for Spring 3.1.1+.
package org.myorg.spring.security;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.FlashMap;
import org.springframework.web.servlet.FlashMapManager;
import org.springframework.web.servlet.support.SessionFlashMapManager;
@Component
public final class FlashAuthenticationFailureHandler implements AuthenticationFailureHandler
{
/**
* Flash attribute name to save on redirect.
*/
public static final String AUTHENTICATION_MESSAGE = "FLASH_AUTHENTICATION_MESSAGE";
public FlashAuthenticationFailureHandler()
{
return;
}
@Override
public void onAuthenticationFailure(final HttpServletRequest request, final HttpServletResponse response, final AuthenticationException exception) throws IOException, ServletException
{
if (exception != null)
{
final FlashMap flashMap = new FlashMap();
// Don't send the AuthenticationException object itself because it has no default constructor and cannot be re-instantiated.
flashMap.put(AUTHENTICATION_MESSAGE, exception.getMessage());
final FlashMapManager flashMapManager = new SessionFlashMapManager();
flashMapManager.saveOutputFlashMap(flashMap, request, response);
}
response.sendRedirect(request.getHeader("referer"));
return;
}
}
Then in the controller(s) that requires the flash attribute, simply add a ModelAttribute with the same name:
@RequestMapping(value = {"/someview"}, method = RequestMethod.GET)
public String getSomePage(final Authentication authentication, @ModelAttribute(FlashAuthenticationFailureHandler.AUTHENTICATION_MESSAGE) final String authenticationMessage, final Model model) throws Exception
{
if (authenticationMessage != null)
{
model.addAttribute("loginMessage", authenticationMessage);
}
return "myviewname";
}
Then the page attribute containing the message can be accessed in your JSP as follows:
<c:if test="${not empty loginMessage}">
<div class="alert alert-danger"><c:out value="${loginMessage}" /></div>
</c:if>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With