jsfauthenticationviewexpiredexception

ViewExpiredException on Wildfly Openshift


I had hosted sample J2EE webapp on Openshift wildfly 8.1, where I am getting ViewExpiredException. After, providing the error-page and redirecting to Login page on this exception. The Login page is still not able to re-direct to the protected pages after successful login.

Here is the app http://wildfly81-sysguard.rhcloud.com login with (user1/1234)

and please find code below.

faces-config.xml

<?xml version="1.0" encoding="UTF-8"?>

<faces-config
    xmlns="http://java.sun.com/xml/ns/javaee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-facesconfig_2_0.xsd"
    version="2.0">
    <navigation-rule>
        <display-name>Login.xhtml</display-name>
        <from-view-id>/Login.xhtml</from-view-id>
        <navigation-case>
            <from-outcome>failure</from-outcome>
            <to-view-id>/Login.xhtml</to-view-id>
            <redirect />
        </navigation-case>
    </navigation-rule>
    <factory>
        <exception-handler-factory>org.omnifaces.exceptionhandler.FullAjaxExceptionHandlerFactory</exception-handler-factory>
    </factory>
</faces-config>

web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
    <display-name>prime</display-name>
    <servlet>
        <servlet-name>Faces Servlet</servlet-name>
        <servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>Faces Servlet</servlet-name>
        <url-pattern>*.xhtml</url-pattern>
    </servlet-mapping>
    <welcome-file-list>
        <welcome-file>/Login.xhtml</welcome-file>
    </welcome-file-list>
    <security-constraint>
        <display-name>User</display-name>
        <web-resource-collection>
            <web-resource-name>User Operations</web-resource-name>
            <description/>
            <url-pattern>/user/*</url-pattern>
        </web-resource-collection>
        <auth-constraint>
            <description>User</description>
            <role-name>Administrator</role-name>
            <role-name>User</role-name>
        </auth-constraint>
    </security-constraint>
    <login-config>
        <auth-method>FORM</auth-method>
        <realm-name>jdbc-security-domain</realm-name>
        <form-login-config>
            <form-login-page>/Login.xhtml</form-login-page>
            <form-error-page>/Login.xhtml</form-error-page>
        </form-login-config>
    </login-config>
    <security-role>
        <role-name>Administrator</role-name>
    </security-role>
    <security-role>
        <description/>
        <role-name>User</role-name>
    </security-role>
    <error-page>
        <exception-type>javax.faces.application.ViewExpiredException</exception-type>
        <location>/Login.xhtml</location>
    </error-page>

    <mime-mapping>
        <extension>woff</extension>
        <mime-type>font/woff</mime-type>
    </mime-mapping>
    <mime-mapping>
       <extension>ico</extension>
       <mime-type>image/x-icon</mime-type>
    </mime-mapping>
</web-app>

jboss-web.xml

<?xml version="1.0" encoding="UTF-8"?>
<jboss-web>
    <context-root>/</context-root>
    <security-domain>java:/jaas/jdbc-security-domain</security-domain>
</jboss-web>

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>wildfly81</groupId>
    <artifactId>wildfly81</artifactId>
    <packaging>war</packaging>
    <version>1.0</version>
    <name>wildfly81</name>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>1.7</maven.compiler.source>
        <maven.compiler.target>1.7</maven.compiler.target>

    </properties>

    <dependencies>

        <dependency>
            <groupId>org.primefaces</groupId>
            <artifactId>primefaces</artifactId>
            <version>5.0</version>
        </dependency>
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-entitymanager</artifactId>
            <version>4.3.6.Final</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>javax</groupId>
            <artifactId>javaee-api</artifactId>
            <version>7.0</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.omnifaces</groupId>
            <artifactId>omnifaces</artifactId>
            <version>1.8.1</version>
        </dependency>
    </dependencies>

    <profiles>
        <profile>
            <!-- When built in OpenShift the 'openshift' profile will be used when 
                invoking mvn. -->
            <!-- Use this profile for any OpenShift specific customization your app 
                will need. -->
            <!-- By default that is to put the resulting archive into the 'deployments' 
                folder. -->
            <!-- http://maven.apache.org/guides/mini/guide-building-for-different-environments.html -->
            <id>openshift</id>
            <build>
                <finalName>prime</finalName>
                <plugins>
                    <plugin>
                        <groupId>org.apache.maven.plugins</groupId>
                        <artifactId>maven-war-plugin</artifactId>
                        <version>2.3</version>
                        <configuration>
                            <failOnMissingWebXml>false</failOnMissingWebXml>
                            <outputDirectory>deployments</outputDirectory>
                            <warName>ROOT</warName>
                        </configuration>
                    </plugin>
                </plugins>
            </build>
        </profile>
    </profiles>
    <repositories>
        <repository>
            <url>http://repository.primefaces.org/</url>
            <id>PrimeFaces-maven-lib</id>
            <layout>default</layout>
            <name>Repository for library PrimeFaces-maven-lib</name>
        </repository>
    </repositories>

</project>

Login.xhtml

<ui:composition template="/templates/plain.xhtml"
    xmlns="http://www.w3.org/1999/xhtml"
    xmlns:h="http://java.sun.com/jsf/html"
    xmlns:f="http://java.sun.com/jsf/core"
    xmlns:ui="http://java.sun.com/jsf/facelets"
    xmlns:p="http://primefaces.org/ui">

    <ui:define name="content">
        <h:form id="loginForm">
            <p:panelGrid columns="3">
                <p:outputLabel for="username" value="Username" />
                <p:inputText id="username" value="#{loginController.username}">
                    <f:validateLength minimum="1" />
                </p:inputText>
                <h:message class="error" for="username" />

                <p:outputLabel for="password" value="Password" />
                <p:password id="password" value="#{loginController.password}"
                    required="true">
                    <f:validateLength minimum="1" />
                </p:password>

            </p:panelGrid>

            <h:message class="error" for="password" />
            <p:commandButton styleClass="button" value="Login" update="@form"
                actionListener="#{loginController.login}" type="submit" />


        </h:form>

    </ui:define>
</ui:composition>

LoginPageFilter.java

package com.prime.filter;

import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.faces.application.ResourceHandler;
import javax.inject.Inject;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * To prevent user from going back to Login page if the user already logged in
 *
 * @author prime
 */
@WebFilter(urlPatterns="/Login.xhtml")
public class LoginPageFilter implements Filter {

    @Inject
    private transient Logger logger;

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        HttpServletResponse response = (HttpServletResponse) servletResponse;

        if (request.getUserPrincipal() != null) { //If user is already authenticated

            String navigateString = "/user/expirepage.xhtml";

            String url = request.getContextPath() + navigateString;
            response.sendRedirect(url);
            logger.log(Level.INFO, "Redirect to {0}",url);
        } else {
            if (!request.getRequestURI().startsWith(request.getContextPath() + ResourceHandler.RESOURCE_IDENTIFIER)) {
                response.setHeader("Cache-Control", "no-cache, no-store, must-revalidate");
                response.setHeader("Pragma", "no-cache");
                response.setDateHeader("Expires", 0); 
                logger.log(Level.INFO, "Clear Cache {0}",response.toString());
            }   
            filterChain.doFilter(servletRequest, response);
        }
    }

    @Override
    public void destroy() {
    }

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
    }

}

LoginController.java

package com.prime.controller;

import java.io.IOException;
import java.io.Serializable;
import java.security.MessageDigest;
import java.security.Principal;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.enterprise.context.SessionScoped;
import javax.faces.context.FacesContext;
import javax.faces.event.ActionEvent;
import javax.inject.Inject;
import javax.inject.Named;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import javax.validation.constraints.NotNull;

import com.prime.util.DateUtility;

/**
 * Login Controller class allows only authenticated users to log in to the web
 * application.
 *
 * @author prime
 */
@Named
@SessionScoped
public class LoginController implements Serializable {

    private static final long serialVersionUID = 1L;

    @Inject
    private transient Logger logger;

    @Inject
    FacesContext facesContext;

    @NotNull
    private String username;

    @NotNull
    private String password;

    private String email; 

    /**
     * Creates a new instance of LoginController
     */
    public LoginController() {
    }

    //  Getters and Setters
    /**
     * @return username
     */
    public String getUsername() {
        return username;
    }

    /**
     *
     * @param username
     */
    public void setUsername(String username) {
        this.username = username;
    }

    /**
     *
     * @return password
     */
    public String getPassword() {
        return password;
    }

    /**
     *
     * @param password
     */
    public void setPassword(String password) {
        this.password = password;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }


    /**
     * Listen for button clicks on the #{loginController.login} action,
     * validates the username and password entered by the user and navigates to
     * the appropriate page.
     *
     * @param actionEvent
     */
    public void login(ActionEvent actionEvent) {

        HttpServletRequest request = (HttpServletRequest) facesContext.getExternalContext().getRequest();
        try {

            try{
                MessageDigest md = java.security.MessageDigest.getInstance("MD5");
                md.update(password.getBytes("UTF-8"));
                byte[] passwordDigest = md.digest();
                @SuppressWarnings("restriction")
                String encodedPasswordHash = new sun.misc.BASE64Encoder().encode(passwordDigest);
                logger.log(Level.INFO, "Encripted Password {0}" ,encodedPasswordHash);
            } catch(Exception e){
                e.printStackTrace();
            }

            // Checks if username and password are valid if not throws a ServletException
            request.login(username, password);
            // gets the user principle and navigates to the appropriate page
            Principal principal = request.getUserPrincipal();

            String navigateString = "/user/expirepage.xhtml"; 

            try {
                logger.log(Level.INFO, "User ({0}) loging in #" + DateUtility.getCurrentDateTime(), request.getUserPrincipal().getName());
                facesContext.getExternalContext().redirect(request.getContextPath() + navigateString);
            } catch (IOException ex) {
                logger.log(Level.SEVERE, "IOException, Login Controller" + "Username : " + principal.getName(), ex);

            }
        } catch (ServletException e) {            
            logger.log(Level.SEVERE, "Login Controller: The username or password you provided does not match our records.");            
            facesContext.validationFailed();
        }
    }

    /**
     * Listen for logout button clicks on the #{loginController.logout} action
     * and navigates to login screen.
     */
    public void logout(ActionEvent actionEvent) {

        HttpSession session = (HttpSession) facesContext.getExternalContext().getSession(false);
        HttpServletRequest request = (HttpServletRequest) facesContext.getExternalContext().getRequest();
        logger.log(Level.INFO, "User ({0}) loging out #" + DateUtility.getCurrentDateTime(), request.getUserPrincipal().getName());
        if (session != null) {
            session.invalidate();
        }
        facesContext.getApplication().getNavigationHandler().handleNavigation(facesContext, null, "/Login.xhtml?faces-redirect=true");

    }


}

Solution

  • The issue was caused due to multiple JSessionid when root context is "/". I had fixed the issue by adding below configuration at jboss-web.xml

    <?xml version="1.0"?>
    <jboss-web>
        <session-config>
           <cookie-config>
              <path>/</path>
           </cookie-config>
        </session-config>
    </jboss-web>
    <xml>