java - JAAS - Java EE 6 中的 Java 编程安全性(没有 @DeclareRoles)

标签 java jax-rs java-ee-6 jboss7.x jaas

Java 安全性是我最近几周的主要话题,我归档了以下内容:

  • 自定义 Valve validator (扩展 AuthenticatorBase)
  • jBoss 自定义登录模块(extends UsernamePasswordLoginModule)
  • 安全端点 (JAX-RS)

我的主要问题是,我的端点仅适用于注释 @DeclareRoles,如果我不使用它,我将无法通过身份验证。详细地说,AuthenticatorBase.invoke 方法(来自 org.apache.catalina.authenticator)调用方法 RealmBase.hasResourcePermission 并在那里检查角色。

由于我不使用任何预定义的角色,因此检查将失败。

我的问题:有没有办法使用这样的代码:

@Path("/secure")
@Stateless
public class SecuredRestEndpoint {  

    @Resource
    SessionContext ctx;

    @GET
    public Response performLogging() {

        // Receive user information
        Principal callerPrincipal = ctx.getCallerPrincipal();
        String userId = callerPrincipal.getName();

        if (ctx.isCallerInRole("ADMIN")) {
            // return 200 if ok
            return Response.status(Status.OK).entity(userId).build();
        }
    ...
    }
}

一些额外的背景:需要使用反向代理进行身份验证,只是转发用户名 (X-FORWARD-USER)。这就是为什么我使用自己的 Authenticator 类和自定义登录模块(我没有任何密码凭据)。但我认为来自应用程序服务器本身的标准身份验证方法也会出现问题

最佳答案

由于您的代码是静态的(意味着您拥有一组可以保护的静态资源),除了增加访问粒度之外,我不明白您对添加安全角色的要求。我可以在代码不是静态的模块化环境中看到此要求,在这种情况下,您将需要支持稍后部署声明的额外安全角色。

也就是说,我必须实现类似的东西,一个支持以下功能的安全系统:

  • 添加(声明)/删除角色而不重新部署;
  • 将用户与这些角色相关联。

我将在较高的抽象层次上描述我所做的事情,希望它能给你一些有用的想法。

首先,实现一个@EJB,像这样:

@Singleton
@LocalBean
public class MySecurityDataManager {

    public void declareRole(String roleName) {
        ...
    }

    public void removeRole(String roleName) {
        ...
    }

    /**
     * Here, I do not know what your incoming user data looks like such as:
     * do they have groups, attributes? In my case I could determine user's groups
     * and then assign them to roles based on those. You may have some sort of
     * other attribute or just plain username to security role association.
     */
    public void associate(Object userAttribute, String roleName) {
        ...
    }

    public void disassociate(Object userAttribute, String roleName) {
        ...
    }

    /**
     * Here basically you inspect whatever persistence method you chose and examine
     * your existing associations to build a set of assigned security roles for a
     * user based on the given attribute(s).
     */
    public Set<String> determineSecurityRoles(Object userAttribute) {
        ...
    }
}

然后您实现自定义 javax.security.auth.spi.LoginModule。我建议从头开始实现它,除非你知道容器提供的抽象实现对你有用,但它不适合我。另外,如果您不熟悉以下内容,我建议您熟悉以下内容,以便更好地理解我要讲的内容:


public class MyLoginModule implements LoginModule {

    private MySecurityDataManager srm;

    @Override
    public void initialize(Subject subject, CallbackHandler callbackHandler,
            Map<String, ?> sharedState, Map<String, ?> options) {
        // make sure to save subject, callbackHandler, etc.
        try {
            InitialContext ctx = new InitialContext();
            this.srm = (MySecurityDataManager) ctx.lookup("java:global/${your specific module names go here}/MySecurityDataManager");
        } catch (NamingException e) {
            // error logic
        }
    }

    @Override
    public boolean login() throws LoginException {
        // authenticate your user, see links above
    }

    @Override
    public boolean commit() throws LoginException {
        // here is where user roles get assigned to the subject
        Object userAttribute = yourLogicMethod();
        Set<String> roles = srm.determineSecurityRoles(userAttribute);
        // implement this, it's easy, just make sure to include proper equals() and hashCode(), or just use the Jboss provided implementation.
        Group rolesGroup = new SimpleGroup("Roles", roles);
        // assuming you saved the subject
        this.subject.getPrincipals().add(rolesGroup);
    }

    @Override
    public boolean abort() throws LoginException {
        // see links above
    }

    @Override
    public boolean logout() throws LoginException {
        // see links above
    }

}

为了允许动态配置(即声明角色、关联用户),构建一个使用相同的 @EJB MySecurityDataManager 的 UI 来增删改查登录模块将用于确定安全角色的安全设置.

现在,您可以按照自己的方式打包它们,只需确保 MyLoginModule 可以查找 MySecurityDataManager 并将它们部署到容器中。我在 JBoss 上工作,你提到了 JBoss,所以这也应该适用于你。更健壮的实现会在 LoginModule 的配置中包含查找字符串,然后您可以在运行时从 initialize() 方法中的选项映射中读取它。这是 JBoss 的示例配置:

<security-domain name="mydomain" cache-type="default">
    <authentication>
        <login-module flag="required"
                      code="my.package.MyLoginModule"
                      module="deployment.${your deployment specific info goes here}">
            <module-option name="my.package.MySecurityDataManager"
                           value="java:global/${your deployment specific info goes here}/MySecurityDataManager"/>
        </login-module>
    </authentication>
</security-domain>

此时您可以使用此安全域 mydomain 来管理容器中任何其他部署的安全性。

这里有几个使用场景:

  1. 部署一个新的 .war 并将其分配给 mydomain 安全域。 .war 在其代码中带有预定义的安全注释。您的安全领域最初没有它们,因此没有用户可以登录。但是在部署之后,由于安全角色已被详细记录,您打开您编写的 mydomain 的配置界面并声明这些角色,然后将用户分配给他们。现在他们可以登录了。
  2. 经过几个月的部署,您不再希望用户可以访问 war 的特定部分。从您的 mydomain 中删除与 .war 的那部分相关的安全角色,这样就没有人可以使用它了。

最好的部分,尤其是关于 #2 的部分是没有重新部署。此外,无需编辑 XML 来覆盖使用注释声明的默认安全设置(假设您的界面比那更好)。

干杯! 我很乐意提供更多细节,但就目前而言,这至少应该告诉您是否需要它们。

关于java - JAAS - Java EE 6 中的 Java 编程安全性(没有 @DeclareRoles),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13084517/

相关文章:

尝试运行程序时显示 java.lang.NumberFormatException 错误

java - 英语和 JAX-B 之间的语义/语法冲突

java - Criteria Builder 基于字符串 - 表达式引用为 double - JPA

java - 选择某个CDI事件观察者

java - 从 axis2 中的静态上下文获取参数

Java Remote 终止用户 session

java - 从 html 页面中提取源代码

java - MVEL 或 Drools 是否无法解析其类型为 Map<String,List<Object>> 的事实?

java - 多线程中如何传递上下文

java - 在 Jboss EAP 7.2.0.GA 中处理 JSON 序列化