java安全管理器SecurityManager

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";
};

有非常多可以进行配置的权限

在启用安全管理器的时候,配置遵循以下基本原则:

  1. 没有配置的权限表示没有。
  2. 只能配置有什么权限,不能配置禁止做什么。
  3. 同一种权限可多次配置,取并集。
  4. 统一资源的多种权限可用逗号分割。

具体有哪些权限可以进行选择可以使用查询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;
        });
    }
}

 没有权限的类,当前类根据我们的配置是没有权限的,而上面的A是有权限的,A中有doPrivileged()方法的调用,所有当前类能正确的调用成功,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();
    }

}
发布了162 篇原创文章 · 获赞 44 · 访问量 8854

猜你喜欢

转载自blog.csdn.net/P19777/article/details/103384643