Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Flash Attribute in custom AuthenticationFailureHandler

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");
    }
}
like image 305
Ramps Avatar asked Mar 20 '23 12:03

Ramps


1 Answers

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>
like image 179
vallismortis Avatar answered Apr 01 '23 03:04

vallismortis