使用 JAAS 调用方法时出现 AccessControlException

AccessControlException when invoking method using JAAS

我已经使用 JAAS 构建了一个 client/server 应用程序。登录似乎工作正常,因为登录成功。在尝试向特定方法授予权限时,AccessController 开始强制转换 AccessControlException.

 java.security.AccessControlException: access denied ("myPackage.CustomPermission" "someMethod")

权限-class:

public class CustomPermission extends BasicPermission {

    public CustomPermission(String name) {
        super(name);
    }

    public CustomPermission(String name, String method) {
        super(name, method);
    }
}

我还有一个自定义委托人-class:

public class CustomPrincipal implements Principal {
    private String name;

    public CustomPrincipal(String name) {
        this.name = name;
    }

    public String getName() {
        return this.name;
    }
}

登录时,用户提供用户名和密码。在数据库中,用户名和密码有一个 "user level" 与之关联。这些用户级别用作主体名称,添加到登录时创建的 Subject。我在 LoginModule:

中处理 commit 方法时添加它
public boolean commit() throws LoginException {
    if(status == ConfigValues.NOT || subject == null)
        return false;

    principal = new CustomPrincipal(userlvl);

    if(subject.getPrincipals().add(principal)) {

        username = null;
        password = null;

        status = ConfigValues.COMMIT;

        return true;
    }

    return false;
}

为此,这就是我实例化登录过程的方式:

LoginContext lc = new LoginContext("MyLoginModule", new RemoteCallbackHandler(username, password));

lc.login();

new ServerImpl(lc.getSubject());

然后在 "proxy" 服务器实现中使用 Subject 来检查权限,像这样 (user = lc.getSubject):

public String someMethod() throws RemoteException, PrivilegedActionException {
    return Subject.doAs(user, new PrivilegedExceptionAction<String>() {
        @Override
        public String run() throws PrivilegedActionException, RemoteException, ServerNotActiveException {
            AccessController.checkPermission(new CustomPermission("someMethod"));
            return realServerImplObj.someMethod();
        }
    });
}

.policy-file:

grant codeBase "file:./bin/-" Principal myPackage.CustomPrincipal "user" {
    permission myPackage.CustomPermission "someMethod";
};

"user"当然是您可以登录的用户级别之一。

我尝试添加一些额外的资助,例如:

grant codeBase "file:./bin/-" {
    permission javax.security.auth.AuthPermission "createLoginContext.MyLoginModule";
    permission javax.security.auth.AuthPermission "doAs";
};

我也有 LoginModule 的配置:

MyLoginModule {
    myPackage.MyLoginModule required debug=true;
};

我在之前设置了所有这些属性,当然:编辑:文件位于项目的根目录中

System.setProperty("java.security.auth.login.config", "file:./MyConfig.config");
System.setProperty("java.security.policy", "file:./MyPolicy.policy");

服务器是 运行,参数是 -Djava.security.manager。客户端使用任何参数,也不使用任何配置或策略文件。

我已经尝试删除代码库以查看我的路径是否错误。如果我添加 permissions java.util.AllPermissions,那么一切都很好(但是......当然不行,因为这绝对不是预期的那样)。这里做错了什么?我想这是 Principal-、Permission、.policiy- 和 LoginModule- 实现的组合。

编辑 在 "proxy" 服务器实现中调用 AccessController.checkPermissions(...) 时抛出异常。

编辑 2 我尝试对代码进行以下编辑:

return AccessController.doPrivileged(new PrivilegedExceptionAction<String>() {
    @Override
    public String run() throws PrivilegedActionException, RemoteException, ServerNotActiveException {
        //AccessController.checkPermission(new CustomPermission("someMethod"));
        return realServerImplObj.someMethod();
    }
});

请注意,我已将 Subject.doAs(user, new Privile.... 更改为 AccessController.doPrivileged(new Privilege。 参数中不再解析user,我们使用静态方法AccessController.doPrivileged,而不是Subject.doAs。 另一个注意事项: //AccessController.checkPermission(new CustomPer... 已评论

但现在每个人都可以调用任何方法,因为我们实际上从不检查权限。 就好像 AccessController 从来不知道 .policy-file 中授予的权限。

编辑 3 问题似乎出在 Principal 的实施上。

为了澄清起见,这些是所做的编辑:

@Override
public String someMethod() throws PrivilegedActionException {
    return Subject.doAsPrivileged(user, new PrivilegedExceptionAction<String>() {
        @Override
        public String run() throws PrivilegedActionException, RemoteException, ServerNotActiveException {
            AccessController.checkPermission(new CustomPermission("someMethod"));
            return realServerImplObj.someMethod();
        }
    }, null);
}

区别在于return Subject.doAsPrivileged(user, ... , null);。还要注意末尾的 null。

CustomPrincipal-class、#equals(Object)#hashCode()中实现了两个方法。请参阅 here 了解这两种方法的通用示例,以及一般的示例 Principal 实现。

还添加了(尽管看起来 运行ning 没有,实际上)下面的 .policy-file

grant codeBase "file:./bin/-" {
    permission javax.security.auth.AuthPermission "createLoginContext.MyLoginModule";
    permission javax.security.auth.AuthPermission "doAs";
    permission javax.security.auth.AuthPermission "doAsPrivileged";
};

permission javax.security.auth.AuthPermission "doAsPrivileged";为新增条目

Subject#doAs 对比 Subject#doAsPrivileged

AccessController采用的默认授权算法是基于Permissionintersection:如果all of AccessControlContextProtectionDomains,可能 combinedSubjectPrincipals,有,静态and/or根据Policy生效,检查Permission,求值成功;否则失败。

Subject#doAs 在您的情况下不起作用,因为您的 Permissiongrant 编辑为 ProtectionDomainPrincipal 的组合,但不是到域本身。具体来说,在 AccessController#checkPermission(customPermission) 调用时,有效的 AccessControlContext 包括以下相关(就 Permission 评估而言)帧:

Frame # | ProtectionDomain          | Permissions
--------+---------------------------+---------------------------------------------
 2      | "file:./bin/"             | { CustomPermission("someMethod"),
        | + CustomPrincipal("user") |   permissions statically assigned by default
        |                           |   by the ClassLoader }
--------+---------------------------+---------------------------------------------
 1      | "file:./bin/"             | { AuthPermission(
        |                           |   "createLoginContext.MyLoginModule"),
        |                           |   AuthPermission("doAs"), default as above }
--------+---------------------------+---------------------------------------------

这些框架权限的交集当然不包括所需的 CustomPermission

Subject#doAsPrivileged,另一方面,当给定一个 null AccessControlContext 时,就可以做到这一点,因为它 "trims" 有效上下文的堆栈到它的最顶层帧,即doAsPrivileged 被调用的一个。实际发生的是 null (blank) 上下文被 AccessController 视为好像 它是一个上下文其权限评估产生 AllPermission;换句话说:

AllPermission权限2 = { CustomPermission("someMethod"), 默认值 } ,

这是(除了看似无关的静态分配的最小集合 Permnissions)期望的结果。

当然,在不希望出现这种潜在的任意权限升级的情况下,自定义上下文,其封装域的权限表达了您的 最大 权限集 willing grant (to eg. some Subject), can be passed to doAsPrivileged instead of the null one.

为什么 Principal 实现被强制覆盖 equalshashCode

以下堆栈跟踪片段说明了原因:

at java.lang.Thread.dumpStack(Thread.java:1329)
at com.foo.bar.PrincipalImpl.equals(PrincipalImpl.java:53)
at javax.security.auth.Subject$SecureSet.contains(Subject.java:1201)
at java.util.Collections$SynchronizedCollection.contains(Collections.java:2021)
at java.security.Principal.implies(Principal.java:92)
at sun.security.provider.PolicyFile.addPermissions(PolicyFile.java:1374)
at sun.security.provider.PolicyFile.getPermissions(PolicyFile.java:1228)
at sun.security.provider.PolicyFile.getPermissions(PolicyFile.java:1191)
at sun.security.provider.PolicyFile.getPermissions(PolicyFile.java:1132)
at sun.security.provider.PolicyFile.implies(PolicyFile.java:1086)
at java.security.ProtectionDomain.implies(ProtectionDomain.java:281)
at java.security.AccessControlContext.checkPermission(AccessControlContext.java:450)
at java.security.AccessController.checkPermission(AccessController.java:884)
at java.lang.SecurityManager.checkPermission(SecurityManager.java:549)
...

延伸阅读: