【问题标题】:Tomcat-Jaas - How to retrieve subject?Tomcat-Jaas - 如何检索主题?
【发布时间】:2012-10-15 12:17:21
【问题描述】:

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

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

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

【问题讨论】:

  • 确保你的领域用于你的webapp,然后你可以尝试Servlets API的HttpServletRequest.getUserPrincipal()
  • 我知道它有效,但我需要检索主题才能获得角色主体

标签: jakarta-ee tomcat jaas jaspic


【解决方案1】:

要检索主题,我们可以使用 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 );

【讨论】:

    【解决方案2】:

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

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

    JAAS 规范将成功登录的结果描述为 javax.security.auth.Subject 实例,可以包含零个或多个 java.security.Principal 对象中的返回值 Subject.getPrincipals() 方法。但是,它没有提供任何指导 如何区分描述单个用户的主体(以及 因此适合作为值返回 request.getUserPrincipal() 在 Web 应用程序中)来自 Principal(s) 描述此用户的授权角色。尽可能保持 尽可能独立于底层的 LoginMethod 由 JAAS 执行,执行以下策略 通过这个领域:[...]

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

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

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

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

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

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

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

    【讨论】:

    • 谢谢,但我不明白如何将 Jaas 授权与 tomcat 连接(使用 doas 等)
    • 通常您会编写一个特定于容器的登录模块,该模块利用 JAAS 或您想要的任何技术并履行容器的合同。当您以编程方式登录 (HttpServletRequest#login) 或访问受保护的资源时,将完成身份验证并且容器将意识到这一点。通过 web.xml () 中的约束,诸如网页之类的资源是安全的。通过将请求的角色与经过身份验证的用户拥有的角色进行比较来进行授权。如果匹配,则用户被授权。
    • 附注Tomcat 的 JAASRealm 是 JASPIC 的原型。我没有这方面的经验,但这听起来像是 Servlet 配置文件和 Bridge 配置文件之间的交叉。
    • 感谢您的解释,所以要检索我应该使用 isUserinRole() 函数的角色。但我不明白是否以这种方式在 Jaas Logincontext 中完成授权,如果不是这样,Jaas 不用于授权......
    • 要找回角色,确实可以调用isUserinRole()。然而,JAAS 不会(!)在那个时候动态地做出一些授权决定。它不是那样工作的。进行身份验证时,JAAS(或其他任何技术)应该检查提供的凭据(通常是用户名/密码),如果通过,它将检索角色(例如,来自 LDAP 服务器、数据库、XML 文件等) )。只要用户登录,容器就会记住这些角色。当使用isUserInRole()、@RolesAllowed等时,会检查这些记住的角色。
    猜你喜欢
    • 1970-01-01
    • 2011-07-12
    • 2011-05-05
    • 2011-06-11
    • 1970-01-01
    • 2015-11-01
    • 1970-01-01
    • 1970-01-01
    • 2014-09-08
    相关资源
    最近更新 更多