蹲厕所的熊 转载请注明原创出处,谢谢!
前言
相信很多人在看JDK、Spring、Tomcat源码的时候会发现一个类的踪影,它就是SecurityManager。它经常在代码中以这样的方式出现:
SecurityManager sm = getSecurityManager();
if (sm != null) {
sm.checkPropertyAccess(key);
}
这个功能在我们写代码的时候可以说几乎不会用上,但是为了知识体系的完整,我们还是需要了解什么是SecurityManager。
SecurityManager:安全管理器,它是防范恶意攻击的主要安全卫士。它通过执行运行阶段检查和访问授权,以实施应用所需的安全策略,从而保护资源免受恶意操作的攻击。而它需要根据提供的Java安全策略文件(java policy file)来进行防护。
开启安全管理
默认我们系统中直接运行的Java程序都没有开启SecurityManager,开启它只需要在JVM启动参数中加上 -Djava.security.manager
即可,JVM会在程序启动的时候在 sun.misc.Launcher
类中进行初始化。
public Launcher() {
// ...ignore
// 获取配置的JVM参数 -Djava.security.manager
String var2 = System.getProperty("java.security.manager");
if (var2 != null) {
SecurityManager var3 = null;
if (!"".equals(var2) && !"default".equals(var2)) {
try {
var3 = (SecurityManager)this.loader.loadClass(var2).newInstance();
} catch (Exception var5) {}
} else {
// 使用默认的SecurityManager
var3 = new SecurityManager();
}
// 设置安全管理器
System.setSecurityManager(var3);
}
}
既然系统也是通过System.setSecurityManager的方式设置安全管理器的,我们在测试的时候也可以手动设置,但是还是推荐使用启动参数的方式来进行设置。
policy file
默认开启安全管理类后,JVM会先去 ${java.home}/jre/lib/security
下找到 java.security
获取到其中的一段配置:
# The default is to have a single system-wide policy file,
# and a policy file in the user's home directory.
policy.url.1=file:${java.home}/lib/security/java.policy
policy.url.2=file:${user.home}/.java.policy
这段配置就是policy文件的目标位置,默认位置为 ${java.home}/lib/security/java.policy
。
打开此文件:
// Standard extensions get all permissions by default
grant codeBase "file:${{java.ext.dirs}}/*" {
permission java.security.AllPermission;
};
// default permissions granted to all domains
grant {
// Allows any thread to stop itself using the java.lang.Thread.stop()
// method that takes no argument.
// Note that this permission is granted by default only to remain
// backwards compatible.
// It is strongly recommended that you either remove this permission
// from this policy file or further restrict it to code sources
// that you specify, because Thread.stop() is potentially unsafe.
// See the API specification of java.lang.Thread.stop() for more
// information.
permission java.lang.RuntimePermission "stopThread";
// allows anyone to listen on dynamic ports
permission java.net.SocketPermission "localhost:0", "listen";
// "standard" properies that can be read by anyone
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";
};
文件中分两部分授权:
ext下的文件全部授权。
其他文件授予特定的权限,比如线程可以调用
stop
方法(permission java.lang.RuntimePermission "stopThread"),可以读取系统的java.version
属性(permission java.util.PropertyPermission "java.version", "read")等等。
我们写一个例子尝试授权是否能生效:
public static void main(String[] args) throws Exception {
Thread t = new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
t.start();
t.stop();
System.out.println("stop finish");
}
注释掉java.policy里stopThread的那行,执行main方法,会出现拒绝访问的exception:
Exception in thread "main" java.security.AccessControlException: access denied ("java.lang.RuntimePermission" "stopThread")
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.Thread.stop(Thread.java:843)
at com.github.SecurityManagerTest.main(SecurityManagerTest.java:38)
当然,我们也可以使用自己定义的policy文件,只需要在JVM的启动参数中加上 -Djava.security.policy=/Users/Benjamin/Desktop/my.policy
即可。等号后面是自定义的policy文件地址。
如果读完觉得有收获的话,欢迎点赞、关注、加公众号【蹲厕所的熊】,查阅更多精彩历史!!!




