1
votes

I am transitioning to GlassFish 3.1.2, and can't seem to resolve a problem with authentication.

The agenda is simple: I want a login bean to do programmatic authentication for a user. The authentication seems to work (the code goes past the login() method), but the server ends up shown 403 for the protected resources... Please help :)

Here are more details. There is a pure JSF login page with name/pass pair:

<ui:composition 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" template="/templates/main.xhtml">

    <ui:define name="body">
        <h:form id="form">
            <p:messages />

            <p:panel>
                <h:panelGrid>
                    <h:outputText value="User Name" />
                    <p:inputText value="#{loginBean.userName}" id="userName"
                        required="true" />
                    <p:message for="userName" />

                    <h:outputText value="Password" />
                    <p:password value="#{loginBean.password}" id="password"
                        required="true" />
                    <p:message for="password" />

                </h:panelGrid>

                <h:panelGrid columns="2">
                    <p:commandButton value="Clear"
                        actionListener="#{loginBean.clear()}" ajax="false" />

                    <p:commandButton value="Login" action="#{loginBean.login()}"
                        ajax="false" />
                </h:panelGrid>
            </p:panel>
        </h:form>
    </ui:define>
</ui:composition>

and a bean that carries out the login

@Named("loginBean")
@SessionScoped
public class LoginBean implements Serializable {

    private static final long serialVersionUID = 1L;

    @Inject
    @Named("dao")
    private Dao dao;

    private String userName;

    private String password;

    @Inject
    private UserBean userBean;

    ...

    public String login() {
        HttpServletRequest request = (HttpServletRequest) FacesContext
                .getCurrentInstance().getExternalContext().getRequest();

        try {
            request.login(getUserName(), getPassword());

            Principal principal = request.getUserPrincipal();

            logger.info("Logged in successfully: " + principal);
        } catch (ServletException e) {
            Messages.addError("Invalid user name or password.");
            return null;
        }

        User user = dao.findSingle("SELECT u FROM User AS u WHERE u.name = ?1",
                getUserName());

        if (user == null) {
            logger.severe("Unable to find user record after successful authentication");

            Messages.addError("Unable to load user record");
            try {
                request.logout();
            } catch (ServletException e) {
                logger.log(Level.SEVERE, "Unable to logout after failed login attempt", e);
            }
            return null;
        }

        getUserBean().setUser(user);

        return "/list/list.xhtml?faces-redirect=true";
    }

    ... 

    accessors

    ...
}

Here is my web.xml

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

<web-app version="3.0" 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-app_3_0.xsd">

    <display-name>GM</display-name>

    <context-param>
        <param-name>javax.faces.PROJECT_STAGE</param-name>
        <param-value>Development</param-value>
    </context-param>

    <!-- Faces Servlet -->
    <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>

    <login-config>
        <realm-name>gmRealm</realm-name>
    </login-config>

    <security-role>
        <role-name>user</role-name>
    </security-role>

    <security-role>
        <role-name>admin</role-name>
    </security-role>

    <security-constraint>
        <web-resource-collection>
            <web-resource-name>Protected Area</web-resource-name>
            <url-pattern>/list/*</url-pattern>
        </web-resource-collection>
        <auth-constraint>
            <role-name>user</role-name>
        </auth-constraint>
    </security-constraint>

    <security-constraint>
        <web-resource-collection>
            <web-resource-name>Admin Area</web-resource-name>
            <url-pattern>/admin/*</url-pattern>
        </web-resource-collection>
        <auth-constraint>
            <role-name>admin</role-name>
        </auth-constraint>
    </security-constraint>

    <welcome-file-list>
        <welcome-file>login.xhtml</welcome-file>
    </welcome-file-list>

</web-app>

Here is the folder structure for the project:

Folder structure

I was also able to set up the realm as discussed here: http://blog.gamatam.com/2009/11/jdbc-realm-setup-with-glassfish-v3.html

Nothing fancy, AFAICT.

However, whenever I get to the protected resource after a successful login, I get:

HTTP Status 403 - Access to the requested resource has been denied

Despite the fact that the server log contains the message:

INFO: Logged in successfully: nick

Also note that when I remove "?faces-redirect=true", from the login() method's return value, the initial protected resource is rendered just fine right after the login, but all the subsequent requests to it fail with 403.

Here is what it shows in the debugger:

Debugging view

I also think that I've done my homework:

Performing user authentication in Java EE / JSF using j_security_check

Programmatically control login with Servlet 3.0

JSF 2.0 Simple login page

Glassfish 3 security - Form based authentication using a JDBC Realm

Please help...

2
Have you tried returning "list/list.xhtml?faces-redirect=true" (without the "/") or "list.xhtml?faces-redirect=true" (without "/list")? Also, please show your folder structure to see if there is a problem in the navigation rule.Luiggi Mendoza
Thanks for the advice. Tried both, list/list.xhtml gives the same 403, and list.xhtml, gives "Unable to find matching navigation case with from-view-id '/login.xhtml' for action '#{loginBean.login()}' with outcome 'list.xhtml?faces-redirect=true'"Nick G.
You constraint configuration indicates that users with the role admin have no access to the list directory. Is this what you want? If not you should add the role admin to the Protected area constraint.Matt Handy
Thanks for pointing this out, Matt. Admins are really data admins and don't necessarily need the listing functionality. I am concerned that I get "access denied" for authenticated users with proper role mappings. I am mostly concerned that I can't find what's wrong :)Nick G.

2 Answers

0
votes

Oh, well, I guess this one is too deployment specific. I ended up re-conding the prototype in Spring for Tomcat stack. Works like charm now :).

-1
votes

i think you are not permitted jsf resources in spring security. add following line in security.xml in http tags.

<http .........>
    <intercept-url pattern="/javax.faces.resource/**" access="permitAll"/>
</http>