Question

I'm studying JAAS and I'm implementing a simple example to use in a webapp using Tomcat with a JaasRealm.

Now my problem is that I don't know how to retrieve the subject since code like Subject subject = Subject.getSubject(AccessController.getContext()); always returns null.

I'm using Tomcat 7.0.27. Is there something I've missed? In other terms how can I manage authorization in Java EE with JAAS? For example how can I implement an action within the secure context of JAAS?

Was it helpful?

Solution

i knew that and it works, but I need to retrieve subject to get also roleprincipal

Unfortunately, it doesn't work like that in Java EE. The JAAS Subject is just a "bag of principals", and which of those represents the user/caller principal and/or the role principal(s) is simply not standardized. Every other container does things differently here. The Javadoc for Tomcat's JAASRealm describes this and explains the Tomcat specific convention (emphasis mine):

The JAAS Specification describes the result of a successful login as a javax.security.auth.Subject instance, which can contain zero or more java.security.Principal objects in the return value of the Subject.getPrincipals() method. However, it provides no guidance on how to distinguish Principals that describe the individual user (and are thus appropriate to return as the value of request.getUserPrincipal() in a web application) from the Principal(s) that describe the authorized roles for this user. To maintain as much independence as possible from the underlying LoginMethod implementation executed by JAAS, the following policy is implemented by this Realm: [...]

Besides that, from a Java EE environment you rarely even have access to the JAAS Subject, often not even via vendor specific methods. JAAS is far from the universal standard that you seem to think it is, especially when it concerns Java EE.

The only things which you can access in a portable way are the caller principal and the roles associated with it, but even those do not have to be the exact caller principal that your JAAS login module constructed.

JBoss AS for instance, copies this principal a couple of times using its own classes. So, if your JAAS module stored a kaz.zak.FooPrincipal into the Subject for the user/caller principal, then HttpServletRequest#getUserPrincipal() might return a org.jboss.security.SimplePrincipal. The only thing guaranteed is that getName() on that instance will return the same string.

For some more background on this topic:

The last source basically says the same thing, in different wording;

Although it is possible to use JAAS within Tomcat as an authentication mechanism (JAASRealm), the flexibility of the JAAS framework is lost once the user is authenticated. This is because the principals are used to denote the concepts of "user" and "role", and are no longer available in the security context in which the webapp is executed. The result of the authentication is available only through request.getRemoteUser() and request.isUserInRole().

This reduces the JAAS framework for authorization purposes to a simple user/role system that loses its connection with the Java Security Policy.

OTHER TIPS

To retrieve Subject we can use the combination of LoginModule and Valve. The fact that valves are invoked before authentication kicks in is helping us here. When the valve is invoked it puts the session in ThreadLocal (similar to how JBOSS saves request in ThreadLocal) and later when LoginModule.commit() is invoked it saves the subject into the session.

To configure this add compiled code for class below to a jar and place it under $CATALINA_BASE/lib/

package my.test;

import java.io.IOException;
import java.util.Map;

import javax.security.auth.Subject;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.login.LoginException;
import javax.security.auth.spi.LoginModule;
import javax.servlet.ServletException;

import org.apache.catalina.Session;
import org.apache.catalina.connector.Request;
import org.apache.catalina.connector.Response;
import org.apache.catalina.valves.ValveBase;

/**
 * Use following class to retrieve subject in your HTTPServlet when using Tomcat.
 */
public class ContainerServices extends ValveBase implements LoginModule {

    // Key to revtieve subject from session.
    public static final String SUBJECT_KEY =
        "javax.security.auth.Subject.container";

    /**
     * Session for current thread.
     */
    static InheritableThreadLocal<Session> sessionHolder =
        new InheritableThreadLocal<Session>();

    // JAAS Subject being authenticated.
    private Subject subject;

    // Invoke the value.
    public void invoke(Request request, Response response) throws IOException,
            ServletException {

        sessionHolder.set(request.getSessionInternal(true));

        try {
            // Next in the invocation chain
            getNext().invoke(request, response);
        } finally {
            sessionHolder.remove();
        }
    }

    // Initialize the login module
    public void initialize(Subject subject, CallbackHandler callbackHandler,
        Map<String, ?> sharedState, Map<String, ?> options) {
        this.subject = subject;
    }

    // Store subject to session.
    public boolean commit() throws LoginException {

        Session session = sessionHolder.get();

        if (session != null) {
            session.getSession().setAttribute(ContainerServices.SUBJECT_KEY, subject);
        }

        return true;
    }

    // not used
    public boolean abort() throws LoginException {
        return false;
    }

    // not used
    public boolean login() throws LoginException {
        return true;
    }

    // not used
    public boolean logout() throws LoginException {
        return true;
    }
}

In $CATALINA_BASE/conf/server.xml add following Valve configuration as element's child.

<Valve className="my.test.ContainerServices" />

In jaas.config file add the same class as LoginModule.

DummyAppLogin {
    my.test.ContainerServices required debug=true;
    my.test.DummyAppLoginModule required debug=true;
};

Now after you login, authenticated Subject can be retrieved using following.

session.getAttribute( ContainerServices.SUBJECT_KEY );
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top