我用JAAS构建了一个客户端/服务器应用程序。登录似乎工作正常,因为登录成功。当尝试向特定方法授予权限时,AccessController
开始转换AccessControlException
。
java.security.AccessControlException: access denied ("myPackage.CustomPermission" "someMethod")
权限类:
public class CustomPermission extends BasicPermission {
public CustomPermission(String name) {
super(name);
}
public CustomPermission(String name, String method) {
super(name, method);
}
}
我也有一个自定义的主体类:
public class CustomPrincipal implements Principal {
private String name;
public CustomPrincipal(String name) {
this.name = name;
}
public String getName() {
return this.name;
}
}
登录时,用户提供用户名和密码。在数据库中,用户名和密码具有与之关联的"用户级别"。这些用户级别用作主体名称,添加到登录时创建的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());
然后,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";
};
"用户"当然是您可以登录的用户级别之一。
我试图添加一些额外的赠款,例如:
grant codeBase "file:./bin/-" {
permission javax.security.auth.AuthPermission "createLoginContext.MyLoginModule";
permission javax.security.auth.AuthPermission "doAs";
};
我也有一个登录模块的配置:
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实现的组合。
编辑当在"代理"服务器实现中调用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...
已被评论。
但是现在每个人都可以调用任何方法,因为我们实际上从不检查权限。就好像访问控制器永远不会知道 .policy-file 中授予的权限。
编辑 3问题似乎是校长的实施问题。
为了澄清起见,这些是已进行的编辑:
@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);
.还要注意末尾的空值。
在CustomPrincipal
类中实现了两个方法,#equals(Object)
和#hashCode()
。有关这两种方法的通用示例以及一般Principal
实现的示例,请参阅此处。
还添加了(即使它似乎实际上没有运行)以下 .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
vsSubject#doAsPrivileged
AccessController
采用的默认授权算法基于Permission
交集:如果一个AccessControlContext
的所有ProtectionDomain
,可能与一个Subject
的Principal
s组合,静态和/或根据有效的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
。
另一方面,当给定一个null AccessControlContext
时,Subject#doAsPrivileged
可以解决问题,因为它将有效上下文的堆栈"修剪"到其最顶层的框架,即调用doAsPrivileged
的框架。实际发生的是,null
(空白)上下文被AccessController
视为权限评估产生AllPermission
的上下文;换句话说:
AllPermission
⋂权限帧2= {CustomPermission("someMethod")
, 默认的 } ,
这是(除了看似无关的静态分配Permnission
的最小集合)所需的结果。
当然,在不希望这种潜在的任意权限提升的情况下,可以将自定义上下文传递给doAsPrivileged
,其封装域的权限表示您愿意授予的最大权限集(例如某些Subject
),而不是null
。
为什么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)
...
延伸阅读:
- 默认策略实现和策略文件语法
- Java SE 的安全编码准则 - §9 - 访问控制
- 安全故障排除