SecurityManager是java提供的一种安全管理机制,可以用来控制我们每一个class的一些操作权限,在很多的地方都使用到了这个
比如在我们的System.getProperties()方法中有以下代码
public static String getProperty(String key) {
checkKey(key);
// 获取权限管理器
SecurityManager sm = getSecurityManager();
if (sm != null) {
// 检测调用者是否有权限
sm.checkPropertyAccess(key);
}
return props.getProperty(key);
}
SecurityManager的应用场景
当运行未知的Java程序的时候,该程序可能有恶意代码(删除系统文件、重启系统等),为了防止运行恶意代码对系统产生影响,需要对运行的代码的权限进行控制,这时候就要启用Java安全管理器。
比如如果在tomcat的servlet中如果存在System.exit()这样的代码,那么一个请求发送过来,就会导致我们的虚拟机关闭,tomcat也就关闭了,这种情况下,我们就可以进行权限设置,因为在System.exit()方法中会检查调用的class是否有该权限,如果没有权限就会拒绝。
SecurityManager的配置
默认的安全管理器配置文件是 $JAVA_HOME/jre/lib/security/java.policy,即当未指定配置文件时,将会使用该配置。内容如下:
// 指定某个域的权限
// 授权基于路径在"file:${{java.ext.dirs}}/*"的class和jar包,所有权限。
grant codeBase "file:${{java.ext.dirs}}/*" {
permission java.security.AllPermission;
};
// 所有域的权限
grant {
permission java.lang.RuntimePermission "stopThread";
permission java.net.SocketPermission "localhost:0", "listen";
permission java.util.PropertyPermission "java.version", "read";
permission java.util.PropertyPermission "java.vendor", "read";
permission java.util.PropertyPermission "java.vendor.url", "read";
permission java.util.PropertyPermission "java.class.version", "read";
permission java.util.PropertyPermission "os.name", "read";
permission java.util.PropertyPermission "os.version", "read";
permission java.util.PropertyPermission "os.arch", "read";
permission java.util.PropertyPermission "file.separator", "read";
permission java.util.PropertyPermission "path.separator", "read";
permission java.util.PropertyPermission "line.separator", "read";
permission java.util.PropertyPermission "java.specification.version", "read";
permission java.util.PropertyPermission "java.specification.vendor", "read";
permission java.util.PropertyPermission "java.specification.name", "read";
permission java.util.PropertyPermission "java.vm.specification.version", "read";
permission java.util.PropertyPermission "java.vm.specification.vendor", "read";
permission java.util.PropertyPermission "java.vm.specification.name", "read";
permission java.util.PropertyPermission "java.vm.version", "read";
permission java.util.PropertyPermission "java.vm.vendor", "read";
permission java.util.PropertyPermission "java.vm.name", "read";
};
有非常多可以进行配置的权限
在启用安全管理器的时候,配置遵循以下基本原则:
- 没有配置的权限表示没有。
- 只能配置有什么权限,不能配置禁止做什么。
- 同一种权限可多次配置,取并集。
- 统一资源的多种权限可用逗号分割。
具体有哪些权限可以进行选择可以使用查询javadoc,查询Permission抽象类的所有子类就能知道有哪些权限可以进行配置。
当批量配置的时候,有三种模式:
- directory/ 表示directory目录下的所有.class文件,不包括.jar文件
- directory/* 表示directory目录下的所有的.class及.jar文件
- directory/- 表示directory目录下的所有的.class及.jar文件,包括子目录
需要注意的是:配置路径是基于类加载器加载根路径,比如说如果想让/usr/local/c/com/t/A.class路径这的A这个类有某个权限,A所在包com.t,那么你配置的路径应该是codeBase="file:/usr/local/c/",意思就是该路径下的所有的.class都具有权限,class的全限定名要相对与该路径。
如果另外的一个.class,其路径在file:/usr/local/c/t/com/B.class,按照上面的配置,该类是不会具有权限的,因为codeBase配置的相当于是类加载器的加载路径,这种情况下B.class的类加载路径应该是"/usr/local/c/t",而不是"/usr/local/c/",要想B这种情况也具有权限就需要codeBase="file:/usr/local/c/-",这样就包含了其子路径。
启动SecurityManager
启动程序的时候通过附加参数启动安全管理器:
-Djava.security.manager
若要同时指定配置文件的位置那么示例如下:
-Djava.security.manager -Djava.security.policy="E:/java.policy"
通过AccessController看Java安全模型
引用一个文章:https://blog.csdn.net/jiaotuwoaini/article/details/70176021
我们使用SecurityManager进行安全检查,最终都是使用的AccessController的checkPermission(Permission perm)方法
调用该方法时,对于程序要求的所有访问权限,ACC 决定是否授权的基本算法如下:
1. 如果调用链中的某个调用程序(某个class)没有所需的权限,将抛出 AccessControlException;
2. 若是满足以下情况即被授予权限:
a. 调用程序访问另一个有该权限域里程序的方法,并且此方法标记为有访问“特权”。
b. 调用程序所调用(直接或间接)的后续对象都有上述权限。
Java SDK 给域提供了 doPrivileged 方法,让程序突破当前域权限限制,临时扩大访问权限。
具有权限的类
package com;
import java.security.AccessController;
import java.util.Properties;
import java.security.PrivilegedAction;
public class A {
public void printAllProperties() {
// 当前类有权限,但是调用当前类的当前方法的类可能没有权限
// 如果想要这次调用生效,就需要提权,就使用doPrivileged,让没有权限的类也可以进行调用
AccessController.doPrivileged((PrivilegedAction)() -> {
Properties properties = System.getProperties();
properties.forEach((k, v) -> {
System.out.println("key:" + k + " v:" + v);
});
return null;
});
}
}
下面的B类根据我们的配置是没有权限的,而上面的A是有权限的,A中有doPrivileged()方法的调用,所有B能正确的调用printAllProperties()方法成功。A中如果没有段代码,即使A有这个权限,但是因为B作为调用者并没有这个权限就会抛出异常
package com;
import com.A;
import java.security.AccessController;
import java.security.PrivilegedAction;
public class B{
public static void main(String[] args) {
SecurityManager securityManager = System.getSecurityManager();
try{
if (securityManager != null) {
// 检测有没有读取属性文件的权限
securityManager.checkPropertyAccess("driver.sql");
}
System.out.println("权限验证通过");
}catch(Exception e){
System.out.println("权限验证未通过");
}
A a = new A();
a.printAllProperties();
}
}