Tomcat-Jaas - How to retrieve subject?
Asked Answered
E

2

5

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?

Epididymis answered 25/10, 2012 at 10:20 Comment(2)
Be sure that your realm is used for your webapp, then you can try Servlets API's HttpServletRequest.getUserPrincipal()Greeson
I knew that and it works, but I need to retrieve subject to get also roleprincipalEpididymis
H
10

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.

Hulburt answered 11/11, 2012 at 12:32 Comment(8)
Thanks, but I don't understand how connect the Jaas authorization with tomcat (use of doas etc)Epididymis
Normally you would write a container specific login module, which leverages JAAS or whatever tech you want and fulfills the container's contract. When you programmatically login (HttpServletRequest#login) OR access a protected resource, authentication will be done and the container will be aware of this. Via constraints in web.xml (<security-constraint>) resources like web pages are secured. Authorization happens by comparing the requested roles to the roles that the authenticated user has. If the match, the user is authorized.Hulburt
p.s. Tomcat's JAASRealm is a prototype of JASPIC. I don't have experience with this, but it sounds like something that's a cross between the Servlet Profile and Bridge profile.Hulburt
Thanks for the explanations, so to retrieve the role I should use isUserinRole() function. But I don't understand if in this way authorization is done in the Jaas Logincontext or not and if not so Jaas is not used for authorization...Epididymis
To retrieve the role, you can indeed call isUserinRole(). JAAS however WILL NOT(!) dynamically make some authorization decision at that point. It doesn't work like that. When authentication takes place, JAAS (or again, whatever other technology) is supposed to check the supplied credentials (typically username/password) and if those pass, it will retrieve the roles (from e.g. an LDAP server, Database, XML file, whatever). Those roles are then remembered by the container as long as the user is logged-in. When isUserInRole(), @RolesAllowed etc is used, these remembered roles are checked.Hulburt
Allright thanks again. I understand the authentication part. My doubt came from the fact that in J2Se I instatiate a logincontext and I manage the authorization with a doas() call to an Action and grant permission to a certain role in a policy file. So in the J2EE there's no need of a doAs call??? (In Jaas authorization tutorial there's this line: The Subject that is the result of authentication must be associated with the current access control context and to do that The static doAs method from the Subject class must be called, passing it an authenticated Subject and a PrivilegedAction)Epididymis
Indeed, in Java EE (it's not J2EE), there's no doAs() call and the ACC (if any) is not visible to Java EE code. JAAS can be used to check authentication and to retrieve roles. After that, Java EE security takes over and only uses the name and the collection of roles. If you have more elaborate authorization requirements, there's something called JACC for that. But the bottom line is thus that Java EE does not directly use JAAS. It merely uses some types from JAAS (like Principal) and JASPIC or proprietary login modules can make use of JAAS at their own discretion.Hulburt
p.s. a reference to an (old) source that basically says the same as I said above: kopz.org/public/documents/tomcat/jaasintomcat.htmlHulburt
N
0

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 );
Nessy answered 24/9, 2015 at 3:51 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.