使用 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
采用的默认授权算法是基于Permission
intersection:如果all of AccessControlContext
的 ProtectionDomain
s,可能 combined 和 Subject
的 Principal
s,有,静态and/or根据Policy
生效,检查Permission
,求值成功;否则失败。
Subject#doAs
在您的情况下不起作用,因为您的 Permission
被 grant
编辑为 ProtectionDomain
和 Principal
的组合,但不是到域本身。具体来说,在 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")
, 默认值 } ,
这是(除了看似无关的静态分配的最小集合 Permnission
s)期望的结果。
当然,在不希望出现这种潜在的任意权限升级的情况下,自定义上下文,其封装域的权限表达了您的 最大 权限集 willing grant (to eg. some Subject
), can be passed to doAsPrivileged
instead of the null
one.
为什么 Principal
实现被强制覆盖 equals
和 hashCode
?
以下堆栈跟踪片段说明了原因:
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)
...
延伸阅读:
我已经使用 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
采用的默认授权算法是基于Permission
intersection:如果all of AccessControlContext
的 ProtectionDomain
s,可能 combined 和 Subject
的 Principal
s,有,静态and/or根据Policy
生效,检查Permission
,求值成功;否则失败。
Subject#doAs
在您的情况下不起作用,因为您的 Permission
被 grant
编辑为 ProtectionDomain
和 Principal
的组合,但不是到域本身。具体来说,在 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")
, 默认值 } ,
这是(除了看似无关的静态分配的最小集合 Permnission
s)期望的结果。
当然,在不希望出现这种潜在的任意权限升级的情况下,自定义上下文,其封装域的权限表达了您的 最大 权限集 willing grant (to eg. some Subject
), can be passed to doAsPrivileged
instead of the null
one.
为什么 Principal
实现被强制覆盖 equals
和 hashCode
?
以下堆栈跟踪片段说明了原因:
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)
...
延伸阅读: