2

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()?

BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
cmlonder
  • 2,370
  • 23
  • 35
  • There is no `FacesContext` as you are running outside of the JSF servlet. What isn't there cannot be accessed. Also `@ManagedProperty` in a spring bean will do nothing as `@ManagedProperty` is to be used in JSF managed beans NOT spring managed beans. – M. Deinum Aug 23 '17 at 06:41
  • I know: "Because this class is not in JSF lifecycle, FacesContext.getCurrentInstance() returns null". Than what is the solution for returning message from Spring Security to the JSF servlet? – cmlonder Aug 23 '17 at 06:46
  • 2
    Find some intermediate solution. Store them in the session ad before rendering the page set them as faces messages (or something alike). – M. Deinum Aug 23 '17 at 06:48
  • 2
    Even if you're using JSF for your application, this form completely gets rid of the JSF lifecycle. You're basically performing a POST against a non-JSF endpoint. So, as Deinum says, you could write some kind of hack as using the session scope, for example. Other alternative might be not to use primefaces in your login page and use javascript to display the message from the response. – Aritz Aug 23 '17 at 06:52
  • 1
    Just use Spring way to add a message. – BalusC Aug 23 '17 at 06:55

1 Answers1

0

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"/>
cmlonder
  • 2,370
  • 23
  • 35