5

我正在研究 JAAS,并且正在实现一个简单的示例,以在使用带有 JaasRealm 的 Tomcat 的 web 应用程序中使用。

现在我的问题是我不知道如何检索主题,因为代码Subject subject = Subject.getSubject(AccessController.getContext());总是返回 null。

我正在使用 Tomcat 7.0.27。有什么我错过的吗?换句话说,如何使用 JAAS 在 Java EE 中管理授权?例如,如何在 JAAS 的安全上下文中实施操作?

4

2 回答 2

9

我知道并且它有效,但我需要检索主题才能获得roleprincipal

不幸的是,它在 Java EE 中不是这样工作的。JAAS 主题只是一个“主体包”,其中哪些代表用户/调用者主体和/或角色主体根本没有标准化。每个其他容器在这里做的事情都不同。Tomcat 的 JAASRealm的Javadoc 对此进行了描述并解释了 Tomcat 特定的约定(强调我的):

JAAS 规范将成功登录的结果描述为 javax.security.auth.Subject 实例,该实例可以在 Subject.getPrincipals() 方法的返回值中包含零个或多个 java.security.Principal 对象。但是,它没有提供关于如何区分描述单个用户的委托人(因此适合作为 web 应用程序中 request.getUserPrincipal() 的值返回)与描述此授权角色的委托人的指导用户。为了尽可能多地独立于 JAAS 执行的底层 LoginMethod 实现,此 Realm 实现了以下策略:[...]

除此之外,在 Java EE 环境中,您甚至很少能够访问 JAAS 主题,通常甚至无法通过供应商特定的方法。JAAS 与您认为的通用标准相去甚远,尤其是在涉及 Java EE 时。

您可以以可移植方式访问的唯一内容是调用者主体和与其关联的角色,但即使这些也不必是您的 JAAS 登录模块构造的确切调用者主体。

例如,JBoss AS 使用它自己的类复制这个主体几次。因此,如果您的 JAAS 模块将 a 存储kaz.zak.FooPrincipal到用户/调用者主体的主题中,则HttpServletRequest#getUserPrincipal()可能会返回org.jboss.security.SimplePrincipal. 唯一可以保证的是,getName()在该实例上将返回相同的字符串。

有关此主题的更多背景信息:

最后一个消息来源基本上说的是同一件事,但措辞不同;

尽管可以在 Tomcat 中使用 JAAS 作为身份验证机制 (JAASRealm),但是一旦用户通过身份验证,就会失去 JAAS 框架的灵活性。这是因为主体用于表示“用户”和“角色”的概念,并且在执行 webapp 的安全上下文中不再可用。身份验证的结果只能通过 request.getRemoteUser() 和 request.isUserInRole() 获得。

这将用于授权目的的 JAAS 框架简化为一个简单的用户/角色系统,该系统失去了与 Java 安全策略的连接。

于 2012-11-11T12:32:14.997 回答
0

要检索主题,我们可以使用 LoginModule 和 Valve 的组合。在身份验证开始之前调用阀门的事实在这里对我们有所帮助。当阀门被调用时,它将会话放入 ThreadLocal 中(类似于 JBOSS 如何将请求保存在 ThreadLocal 中),然后当 LoginModule.commit() 被调用时,它将主题保存到会话中。

要配置它,请将下面类的编译代码添加到 jar 中,并将其放在 $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;
    }
}

在 $CATALINA_BASE/conf/server.xml 中添加以下 Valve 配置作为元素的子元素。

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

在 jaas.config 文件中添加与 LoginModule 相同的类。

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

现在登录后,可以使用以下方式检索经过身份验证的主题。

session.getAttribute( ContainerServices.SUBJECT_KEY );
于 2015-09-24T03:51:50.367 回答