启动
默认情况下,Java的安全模型是不启用的。为了使用Java的安全模型,需要通过初始化安全管理器(SecurityManager)来启用Java安全模型。
- 编码式初始化SecurityManager
// 获取安全管理器,如果安全管理器未安装,则返回null
SecurityManager manager = System.getSecurityManager();
if (manager == null) {
// 安装并初始化安全管理器
System.setSecurityManager(new SecurityManager());
}
- JVM启动参数初始化
-Djava.security.manager
没有安装安全管理器的情况下,调用System.getSecurityManager()返回的是null,即可根据 System.getSecurityManager() 返回结果是否为null来判断Java安全模型有没有启用。
安全策略
当启用Java安全模式时,Java默认的安全策略文件为 ${JAVA_HOME}/jre/lib/security/java.policy
可以用JVM启动参数指定安全策略文件
-Djava.security.policy=/Users/developmac/workspace/develop/config/java.policy
使用
SecurityManager 提供了一系列供用户调用的API来做权限检查。SecurityManager的典型用法如下:
SecurityManager security = System.getSecurityManager();
if (security != null) {
security.checkXXX(argument, . . . );
}
一个文件读权限检查的例子
public class Test {
public static void main(String ...args) throws Exception {
if (System.getSecurityManager() == null) {
System.setSecurityManager(new SecurityManager());
}
System.getSecurityManager().checkRead("foo.txt");
}
}
结果
Exception in thread "main" java.security.AccessControlException: access denied ("java.io.FilePermission" "foo.txt" "read")
at java.security.AccessControlContext.checkPermission(AccessControlContext.java:472)
at java.security.AccessController.checkPermission(AccessController.java:884)
at java.lang.SecurityManager.checkPermission(SecurityManager.java:549)
at java.lang.SecurityManager.checkRead(SecurityManager.java:888)
at java.io.FileInputStream.<init>(FileInputStream.java:127)
at java.io.FileInputStream.<init>(FileInputStream.java:93)
at org.learn.agent.security.ReadFile.main(ReadFile.java:15)
java.security.AccessController#doPrivileged(java.security.PrivilegedAction)
AccessController引入了一个doPrivileged()静态方法,只要Caller执行了doPrivileged()方法(注意:这里的Caller还是需要相应权限,但是Caller的Caller就不需要了,如果不用doPrivileged()方法,调用链上所有的Caller都需要权限),那么这个Caller就会被标记为privilege,Java安全模型就不会去检查这个Caller的权限。也就是说,调用doPrivileged()的Caller被授予了特权,这个Caller可以免去权限检查。在进行权限检查的时候,回溯调用链的过程中,一旦遇到被标记为privilege的Caller,那么AccessController将停止向上回溯,权限检查通过。