2
votes

I want to implement a Managed Bean Form Base Authentication, following the Java EE 6 Turorial I got to configure and build all the required elements, such as the login form, the error page, the web.xml security configuration and the Tomcat security realm (JDBC).

What is the Problem? The commandButton that is supposed to invoke the login() method in the Managed Bean is not working, I can see the constructor and the getters getting called but the login method and the setters are not.

What is curious? If I remove all the security related elements from the web.xml, restart the application and go directly to the login.xhtml form the login() method gets indeed called.

Conclusion? There must be something in the JSF implementation that is preventing this Form Based Authentication from working properly if a managed bean is used.

Note: The regular j_security_check Form Based Authentication (without using JSF) is working fine.

Any Ideas?

login.xhtml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html lang="en" xmlns="http://www.w3.org/1999/xhtml"
    xmlns:h="http://java.sun.com/jsf/html"
    xmlns:f="http://java.sun.com/jsf/core">
<f:view>
    <h:head>
        <title>Login Form</title>
    </h:head>
    <h:body>
        <h2>Hello, please log in:</h2>
        <h:form id="loginForm">
            <h:messages style="color:red;margin:8px;" />
            Username: <h:inputText value="#{loginBean.username}" />
            <br />
            Password: <h:inputSecret value="#{loginBean.password}" />
            <br />
            <h:commandButton id="loginButton" value="Login" action="#{loginBean.login}" />
        </h:form>
    </h:body>
</f:view>
</html>

LoginBean.java

package src;

import java.io.Serializable;

import javax.faces.application.FacesMessage;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.SessionScoped;
import javax.faces.context.FacesContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;

@ManagedBean
@SessionScoped
public class LoginBean implements Serializable  {

    /**
     * 
     */
    private static final long serialVersionUID = 2951813936936766650L;

    public LoginBean() {
        System.out.println("LoginBean()");
    }

    private String username;
    private String password;

    public String getUsername() {
        System.out.println("getUsername() returning: " + this.username);
        return this.username;
    }

    public void setUsername(String username) {
        System.out.println("setUserName(" + username + ")");
        this.username = username;
    }

    public String getPassword() {
        System.out.println("getPassword() returning: " + this.password);
        return this.password;
    }

    public void setPassword(String password) {
        System.out.println("setPassword(" + password + ")");
        this.password = password;
    }

    public String dummy() {
        System.out.println("dummy()");
        return "dummy";
    }

    public String login() {
        System.out.println("login()");
        FacesContext context = FacesContext.getCurrentInstance();
        HttpServletRequest request = (HttpServletRequest) context.getExternalContext().getRequest();
        try {
            request.login(this.username, this.password);
        } catch (ServletException e) {
            e.printStackTrace();
            context.addMessage(null, new FacesMessage("Login failed."));
            return "error";
        }
        return "index";
    }

    public void logout() {
        System.out.println("logout()");
        FacesContext context = FacesContext.getCurrentInstance();
        HttpServletRequest request = (HttpServletRequest) context.getExternalContext().getRequest();
        try {
            request.logout();
        } catch (ServletException e) {
            e.printStackTrace();
            context.addMessage(null, new FacesMessage("Logout failed."));
        }
    }

}

web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
    id="WebApp_ID" version="3.0">
    <display-name>FormBasedManagedBeanAuth</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>/faces/*</url-pattern>
    </servlet-mapping>
    <context-param>
        <description>State saving method: 'client' or 'server' (=default). See JSF Specification 2.5.2</description>
        <param-name>javax.faces.STATE_SAVING_METHOD</param-name>
        <param-value>client</param-value>
    </context-param>
    <context-param>
        <param-name>javax.servlet.jsp.jstl.fmt.localizationContext</param-name>
        <param-value>resources.application</param-value>
    </context-param>
    <listener>
        <listener-class>com.sun.faces.config.ConfigureListener</listener-class>
    </listener>
    <context-param>
        <param-name>javax.faces.PROJECT_STAGE</param-name>
        <param-value>Development</param-value>
    </context-param>
    <welcome-file-list>
        <welcome-file>faces/index.xhtml</welcome-file>
    </welcome-file-list>
    <security-constraint>
        <display-name>Tomcat7FormBasedJAAS</display-name>
        <web-resource-collection>
            <web-resource-name>secured</web-resource-name>
            <description />
            <url-pattern>/*</url-pattern>
        </web-resource-collection>
        <auth-constraint>
            <description />
            <role-name>user</role-name>
        </auth-constraint>
    </security-constraint>
    <login-config>
        <auth-method>FORM</auth-method>
        <form-login-config>
            <form-login-page>/faces/login.xhtml</form-login-page>
            <form-error-page>/faces/error.xhtml</form-error-page>
        </form-login-config>
    </login-config>
    <security-role>
        <description />
        <role-name>user</role-name>
    </security-role>
</web-app>

server.xml (tomcat, fragment)

<Realm className="org.apache.catalina.realm.JDBCRealm"
    connectionName="database"
    connectionPassword="password"
    connectionURL="jdbc:mysql://localhost:3306/database"
    driverName="com.mysql.jdbc.Driver"
    roleNameCol="roleName"
    userCredCol="password"
    userNameCol="userName"
    userRoleTable="user_role"
    userTable="user"/>
2

2 Answers

1
votes

Im not an expert in this area by any means and came across this as I had the same problem. What I ended up doing was having a secure area and a nonsecure area in the web.xml like this...

<security-constraint>
    <web-resource-collection>
        <web-resource-name>SecureArea</web-resource-name>
        <url-pattern>/secure/*</url-pattern>
    </web-resource-collection>

So then I had the following URL for login /WAR/login.xhtml and for all the rest of my app /WAR/secure/*

That way the loginBean could be executed. Worked like a charm. Im not sure if there is a better way but this worked for me.

0
votes

Your login.xhtml is the secure area, hence no bean may be accessed by an unauthorized user.

You can proceed as you did in your own answer, or you can define a "public" area, letting everything else secure:

<security-constraint>
    <web-resource-collection>
        <web-resource-name>Public Area</web-resource-name>
        <description>Public Area</description>
        <url-pattern>/public/*</url-pattern>
    </web-resource-collection>
</security-constraint>

now you move login.xhtml to public/login.xhtml and correct the login-config in web.xml:

<login-config>
    <auth-method>FORM</auth-method>
    <realm-name>ALMGCCAdminDB</realm-name>
    <form-login-config>
        <form-login-page>/public/login.xhtml</form-login-page>
        <form-error-page>/public/error.xhtml</form-error-page>
    </form-login-config>
</login-config>