springjsfspring-securitymessagefacescontext

Add JSF Message From Spring Bean


I'm having problem with adding message to FacesContext from Spring component. Here is the senario:

  1. User logins from .xhtml page
  2. Spring Security authorize user
  3. If user success/failed, return message to the UI from Spring managed class (AuthenticationFailureHandler, AuthenticationSuccessHandler), which is primefaces.

I can't add message via:

@Component
public class LoginFailureHandler implements AuthenticationFailureHandler {

    private static Logger logger = LoggerFactory.getLogger(LoginFailureHandler.class);

    @Override
    public void onAuthenticationFailure(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {
        logger.info("Login failed: " + httpServletRequest.getParameter("j_username"), e);
        FacesMessage message = new FacesMessage(FacesMessage.SEVERITY_ERROR, "Login Error", e.getLocalizedMessage());
        FacesContext.getCurrentInstance().addMessage(null, message);
        httpServletResponse.sendRedirect("/timsgui/login.jsf?error=1");
    }
}

Because this class is not in JSF lifecycle, FacesContext.getCurrentInstance() returns null. Here is my jsf page:

<body>

    <p:growl id="growl" sticky="true" showDetail="true" life="3000" />

    <h:form name="login" action="../j_spring_security_check" method="POST">
        <h:panelGrid>
            <h:outputLabel for="j_username" value="Username:"/>
            <p:inputText id="j_username"/>

            <h:outputLabel for="j_password" value="Password:"/>
            <p:password id="j_password"/>

            <!-- need CSRF protection which is enabled default after Spring 4-->
            <input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}"/>
            <p:commandButton value="Login" update="growl" type="submit"/>
        </h:panelGrid>
    </h:form>

I tried to inject FacesContext into spring bean but it still returns null:

@ManagedProperty("#{facesContext}")

Is there anyway that i can inject current FacesContext to the Spring bean or any other ideas that i can add that global message from Spring Bean to FacesContext without FacesContext.getCurrentInstance().addMessage()?


Solution

  • Okay, i solved this. Thanks @M.Delinum and @Xtreme Biker for your suggestions. I injected message into HttpSession and get it from http scope. Here is final code:

    @Component
    public class LoginFailureHandler implements AuthenticationFailureHandler {
    
        private static Logger logger = LoggerFactory.getLogger(LoginFailureHandler.class);
    
        @Override
        public void onAuthenticationFailure(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {
            logger.info("Login failed: " + httpServletRequest.getParameter("j_username"), e);
            HttpSession session = httpServletRequest.getSession(false);
            if (session != null) {
                session.setAttribute("loginFailMessage", "Login is failed");
            }
            httpServletResponse.sendRedirect("/timsgui/login.jsf?error=true");
        }
    }
    
    <h:outputText value="#{sessionScope['loginFailMessage']}" rendered="#{not empty sessionScope['loginFailMessage']}" styleClass="highlight"/>