SecurityManager——Java 语言的保护伞

一、引例

今天在阅读 Thread 类的源码的时候,看到了如下一段代码:

SecurityManager security = System.getSecurityManager();

这个和 Security 有关的对象阻碍了我阅读的脚步,并且成功地吸引了我的注意。下面就来探讨一下这个类到底有什么样的魔力。

二、定义

当运行未知的 Java 程序的时候,该程序可能有恶意代码(删除系统文件、重启系统等),为了防止运行恶意代码对系统产生影响,需要对运行的代码的权限进行控制,这时候就要启用 Java 安全管理器。

在 JDK 1.8 中默认的安全管理器配置文件是 $JAVA_HOME/jre/lib/security/java.policy,即当未指定配置文件时,将会使用该配置。

安全管理器在 Java 语言中的作用就是检查操作是否有权限执行。是 Java 沙箱的基础组件。我们一般所说的打开沙箱,也是加 -Djava.security.manager 选项。

其实日常的很多 API 都涉及到安全管理器,它的工作原理一般是:

  1. 请求 Java API
  2. Java API 使用安全管理器判断许可权限
  3. 通过则顺序执行,否则抛出一个 Exception

三、属性和方法

属性

public class SecurityManager {

    // 进程中是否有安全检查
    @Deprecated
    protected boolean inCheck;

    // 是否初始化
    private boolean initialized = false;
    
    // 线程组
    private static ThreadGroup rootGroup = getRootGroup();
    
    private static boolean packageAccessValid = false;
    private static String[] packageAccess;
    private static final Object packageAccessLock = new Object();

    private static boolean packageDefinitionValid = false;
    private static String[] packageDefinition;
    private static final Object packageDefinitionLock = new Object();
    
}

SecurityManager 中属性比较少,主要是一些标志位。

方法

SecurityManager——Java 语言的保护伞_第1张图片

SecurityManager——Java 语言的保护伞_第2张图片

SecurityManager 中的方法除了获取一些信息外,剩下的基本都是 check 方法,分别囊括了文件的读写删除和执行、网络的连接和监听、线程的访问、以及其他包括打印机剪贴板等系统功能。而这些 check 代码也基本横叉到了所有的核心 Java API 上。正是这些方法的存在,使得我们的 Java 程序能够抵挡外来恶意代码的攻击,可以说 SecurityManager 就是 Java 语言的保护伞。

四、启动安全管理器

启动安全管理有两种方式,建议使用启动参数方式。

1.启动参数方式

启动程序的时候通过附加参数启动安全管理器:

-Djava.security.manager

若要同时指定配置文件的位置那么示例如下:

-Djava.security.manager -Djava.security.policy="C:/java.policy"

2.编码方式启动

也可以通过编码方式启动,不过不建议:

System.setSecurityManager(new SecurityManager());

通过参数启动,本质上也是通过编码启动,不过参数启动使用灵活,项目启动源码如下(sun.misc.Launcher):

// Finally, install a security manager if requested
String s = System.getProperty("java.security.manager");
if (s != null) {
    SecurityManager sm = null;
    if ("".equals(s) || "default".equals(s)) {
        sm = new java.lang.SecurityManager();
    } else {
        try {
            sm = (SecurityManager)loader.loadClass(s).newInstance();
        } catch (IllegalAccessException e) {
        } catch (InstantiationException e) {
        } catch (ClassNotFoundException e) {
        } catch (ClassCastException e) {
        }
    }
    if (sm != null) {
        System.setSecurityManager(sm);
    } else {
        throw new InternalError(
            "Could not create SecurityManager: " + s);
    }
}

五、自定义安全管理器

安全管理器可以自定义,作为核心 API 调用的部分,我们可以自己为自己的业务定制安全管理逻辑。举个例子如下:

public class SecurityManagerTest {
    
    static class MySecurityManager extends SecurityManager {
        @Override
        public void checkExit(int status) {
            throw new SecurityException("no exit");
        }

    }

    public static void main(String[] args) {
        MySecurityManager sm = new MySecurityManager();
        System.out.println(System.getSecurityManager());
        System.setSecurityManager(sm);  // 注释掉测一下
        System.exit(0);
    }
    
}

注释掉代码中的注释行,系统打印 null,然后正常退出。当我们打开注释,并且自己扩展一个 SecurityManager ——MySecurityManager,它做的事情很简单,就是覆盖了 checkExit() 方法,在系统退出时抛出一个“no exit”的异常。再执行,结果变成了

Exception in thread "main" java.lang.SecurityException: no exit
	at com.ml.SecurityManagerTest$MySecurityManager.checkExit(SecurityManagerTest.java:11)
	at java.lang.Runtime.exit(Runtime.java:107)
	at java.lang.System.exit(System.java:971)
	at com.ml.SecurityManagerTest.main(SecurityManagerTest.java:20)
null

显然,安全管理器生效了。

参考:

https://yq.aliyun.com/articles/57223?&utm_source=qq

你可能感兴趣的:(Java)