专注于 JetBrains IDEA 全家桶,永久激活,教程
持续更新 PyCharm,IDEA,WebStorm,PhpStorm,DataGrip,RubyMine,CLion,AppCode 永久激活教程

SecurityManager 权限控制机制详解:Java 安全策略配置与实战案例分析

SecurityManager

前言

对于 SecurityManager 来说, 在读底层源码时经常会遇到它, 但从没仔细研究它是用来干嘛的, 以及之前在 RMI 学习时也曾主动配置过 SecurityManager. 本着读源码就读个痛快的缘故因此在此折腾一下 SecurityManager.

简介

SecurityManager 是 Java内置的一种安全机制, 默认处于禁用状态.

其在 JDK 17 中将其移除了, 具体可以参考 JEP 411: https://openjdk.org/jeps/411 以及 JEP 486: https://openjdk.org/jeps/486

但由于 Java 项目大多数会遇到 JDK 17 以下版本的项目, 所以在这里有必要对其进行一个研究.

初识

我们可以打开java.io.File类中查看它的一些相关方法, 可以发现大多数都使用了SecurityManager进行校验, 代码案例图如下:

img_1

在其中我们可以看到的是, 底层函数使用System.getSecurityManger()进行得到一个SecurityManager, 随后调用它的方法进行一系列验证, 而有趣的是它经常判断if(security != null), 那么什么时候SecurityManagerNULL, 什么时候不为NULL, 它又是从何而来的?

Launcher

在之前研究ClassLoader中我们是通过引入sun.misc.Launcher类来进行观察AppClassLoader, ExtClassLoader, AppClassLoader进行研究的, 而在这边我们依然重新找到它, 并且进行观察它的源码设计如下:

img_2

上述折叠部分是ClassLoader的初始化逻辑, 而使用绿框圈出来的是SecurityManager的初始化逻辑, 在这里提前说明一点: System.getProperty读取-D参数的值, 例如: java -D参数KEY=参数VALUE 类名, 以下是一个具体的案例:

img_3

设置与获取 SecurityManager

那么通过该案例与Launcher类结合起来看即可知道, 一个SecurityManager想要使用可以使用如下两点:

  • 增加 -Djava.security.manager 参数或 -Djava.security.manager=default 采用系统默认的
  • 增加 -Djava.security.manager=类名 来加载自定义的 SecurityManager

而 SecurityManager 的初始阶段由System.setSecurityManager(以上两种情况)来进行设置到系统中的, 随后在我们调用自己的类中的 main 方法时即可通过System.getSecurityManager()来进行获取, 以下是这几种方式的设置案例.

1、 没有任何参数的情况

![img\_4][img_4]

2、 指明了 -Djava.security.manager[=default] 情况

![img\_5][img_5]

3、 指明了 -Djava.security.manager=自定义SecurityManager 情况

![img\_6][img_6]

当然也可以通过SecurityManager.setSecurityManager进行设置自己想要的SecurityManager. 因为无论是否带参数了, 最终都会调用SecurityManager.setSecurityManager.

policy 配置文件

当运行未知的Java程序的时候,该程序可能有恶意代码(删除系统文件、重启系统等),为了防止运行恶意代码对系统产生影响,需要对运行的代码的权限进行控制,这时候就要启用Java安全管理器。而通常默认的 安全管理器配置文件 为jdk8u131\jre\lib\security\java.security & jdk8u131\jre\lib\security\java.policy, 如图:

img_7

首先我们看一下java.security配置文件的内容 (删除注释后的), 如下:

securerandom.strongAlgorithms=Windows-PRNG:SunMSCAPI,SHA1PRNG:SUN
login.configuration.provider=sun.security.provider.ConfigFile
policy.provider=sun.security.provider.PolicyFile
policy.url.1=file:${java.home}/lib/security/java.policy
policy.url.2=file:${user.home}/.java.policy
policy.expandProperties=true
policy.allowSystemProperty=true
policy.ignoreIdentityScope=false
keystore.type=jks
keystore.type.compat=true
package.access=sun.,com.sun.xml.internal.,com.sun.imageio.,com.sun.istack.internal.,com.sun.jmx.,com.sun.media.sound.,com.sun.naming.internal.,com.sun.proxy.,com.sun.corba.se.,com.sun.org.apache.bcel.internal.,com.sun.org.apache.regexp.internal.,com.sun.org.apache.xerces.internal.,com.sun.org.apache.xpath.internal.,com.sun.org.apache.xalan.internal.extensions.,com.sun.org.apache.xalan.internal.lib.,com.sun.org.apache.xalan.internal.res.,com.sun.org.apache.xalan.internal.templates.,com.sun.org.apache.xalan.internal.utils.,com.sun.org.apache.xalan.internal.xslt.,com.sun.org.apache.xalan.internal.xsltc.cmdline.,com.sun.org.apache.xalan.internal.xsltc.compiler.,com.sun.org.apache.xalan.internal.xsltc.trax.,com.sun.org.apache.xalan.internal.xsltc.util.,com.sun.org.apache.xml.internal.res.,com.sun.org.apache.xml.internal.security.,com.sun.org.apache.xml.internal.serializer.utils.,com.sun.org.apache.xml.internal.utils.,com.sun.org.glassfish.,com.oracle.xmlns.internal.,com.oracle.webservices.internal.,oracle.jrockit.jfr.,org.jcp.xml.dsig.internal.,jdk.internal.,jdk.nashorn.internal.,jdk.nashorn.tools.,com.sun.activation.registries.,com.sun.java.accessibility.,com.sun.browser.,com.sun.glass.,com.sun.javafx.,com.sun.media.,com.sun.openpisces.,com.sun.prism.,com.sun.scenario.,com.sun.t2k.,com.sun.pisces.,com.sun.webkit.,jdk.management.resource.internal.
package.definition=sun.,com.sun.xml.internal.,com.sun.imageio.,com.sun.istack.internal.,com.sun.jmx.,com.sun.media.sound.,com.sun.naming.internal.,com.sun.proxy.,com.sun.corba.se.,com.sun.org.apache.bcel.internal.,com.sun.org.apache.regexp.internal.,com.sun.org.apache.xerces.internal.,com.sun.org.apache.xpath.internal.,com.sun.org.apache.xalan.internal.extensions.,com.sun.org.apache.xalan.internal.lib.,com.sun.org.apache.xalan.internal.res.,com.sun.org.apache.xalan.internal.templates.,com.sun.org.apache.xalan.internal.utils.,com.sun.org.apache.xalan.internal.xslt.,com.sun.org.apache.xalan.internal.xsltc.cmdline.,com.sun.org.apache.xalan.internal.xsltc.compiler.,com.sun.org.apache.xalan.internal.xsltc.trax.,com.sun.org.apache.xalan.internal.xsltc.util.,com.sun.org.apache.xml.internal.res.,com.sun.org.apache.xml.internal.security.,com.sun.org.apache.xml.internal.serializer.utils.,com.sun.org.apache.xml.internal.utils.,com.sun.org.glassfish.,com.oracle.xmlns.internal.,com.oracle.webservices.internal.,oracle.jrockit.jfr.,org.jcp.xml.dsig.internal.,jdk.internal.,jdk.nashorn.internal.,jdk.nashorn.tools.,com.sun.activation.registries.,com.sun.java.accessibility.,com.sun.browser.,com.sun.glass.,com.sun.javafx.,com.sun.media.,com.sun.openpisces.,com.sun.prism.,com.sun.scenario.,com.sun.t2k.,com.sun.pisces.,com.sun.webkit.,jdk.management.resource.internal.
security.overridePropertiesFile=true
ssl.KeyManagerFactory.algorithm=SunX509
ssl.TrustManagerFactory.algorithm=PKIX
networkaddress.cache.negative.ttl=10
krb5.kdc.bad.policy=tryLast
jdk.certpath.disabledAlgorithms=MD2, MD5, RSA keySize < 1024, DSA keySize < 1024, EC keySize < 224
jdk.jar.disabledAlgorithms=MD2, MD5, RSA keySize < 1024
jdk.tls.disabledAlgorithms=SSLv3, RC4, MD5withRSA, DH keySize < 768, EC keySize < 224
jdk.tls.legacyAlgorithms=K_NULL, C_NULL, M_NULL, DHE_DSS_EXPORT, DHE_RSA_EXPORT, DH_anon_EXPORT, DH_DSS_EXPORT, DH_RSA_EXPORT, RSA_EXPORT, DH_anon, ECDH_anon, RC4_128, RC4_40, DES_CBC, DES40_CBC, 3DES_EDE_CBC
jdk.xml.dsig.secureValidationPolicy=disallowAlg http://www.w3.org/TR/1999/REC-xslt-19991116,disallowAlg http://www.w3.org/2001/04/xmldsig-more#rsa-md5,disallowAlg http://www.w3.org/2001/04/xmldsig-more#hmac-md5,disallowAlg http://www.w3.org/2001/04/xmldsig-more#md5,maxTransforms 5,maxReferences 30,disallowReferenceUriSchemes file http https,minKeySize RSA 1024,minKeySize DSA 1024,noDuplicateIds,noRetrievalMethodLoops

其中我们可以看到定义的policy.url.1中指明了file:${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";
};

该文件具体是做什么的后面我们会进行说明, 那么我们不禁思考一个问题, 它们是从哪里加载进来的?

底层处理逻辑

Security 类【policy 文件查找规则】

java.security.Security类中, 定位到它的initialize方法, 我们可以看到对java.security文件的初始化读取工作:

img_8

所以Security这个类我们暂时可以理解为基于java.security文件Properties工具类, 我们可以创建如下代码进行验证说法:

img_9

java.security文件的读取我们已经知道被哪个核心类使用了, 接下来我们要看的是java.policy文件是在哪里被使用了, 其中就会涉及到我们案例中举例的policy.provider这个配置项的值.

ORBUtility 类【policy解析逻辑】

com.sun.corba.se.impl.orbutil.ORBUtility这个类从哪里进行初始化的, 我们并不关心, 我们只需要知道从哪里使用了policy.provider配置项的值即可, 在它的getClassSecurityInfo方法中, 如图:

img_10

sun.security.provider.PolicyFile 类【自定义 policy & 系统默认 policy】

实际上这里通过反射将sun.security.provider.PolicyFile通过反射进行初始化了, 我们可以编写如下测试代码进行模拟调用Policy.getPolicy()方法并查看它的返回结果, 不出意外的话会返回一个sun.security.provider.PolicyFile对象:

Policy policy = Policy.getPolicy();
System.out.println(policy); // sun.security.provider.PolicyFile@74a14482

那么我们接着看一下该类的初始化代码做了一些什么事情:

img_11

其具体的解析逻辑不是我们关注的, 从中我们可以看到的是, 如果指明了-Djava.security.policy文件, 那么则会调用PolicyFile.init方法进行读取. 那如果没有指明-Djava.security.policy参数, 实际上initPolicyFile方法中也有它的另一个分支, 如图:

img_12

这里我们可以看到默认的加载逻辑了, 没有传递-Djava.security.policy=配置文件时, 则会调用到默认的file:${java.home}/lib/security/java.policy & file:${user.home}/.java.policy.

所以通常如果我们想要使用系统默认的SecurityManager则无需指明-Djava.security.policy=配置文件, 而如果我们想要自定义配置文件时, 我们需要指明该参数.

正常使用方法

简单 Demo

我们先使用一个简单的案例进行演示, 首先是没有开启SecurityManager的案例, 如下:

package com.heihu577;
public class Main {
    public static void main(String[] args) {
        System.out.println("当前 JDK 版本: " + System.getProperty("java.version"));
        System.setProperty("java.version", "你猜~");
        System.out.println("修改后的 JDK 版本: " + System.getProperty("java.version"));
        /*
         当前 JDK 版本: 1.8.0_131
   修改后的 JDK 版本: 你猜~
        */
    }
}

在没有SecurityManager参与的情况下, 我们成功修改了java.version. 那么如果我们引入SecurityManager之后呢?

img_13

直接抛出了异常. 后面我们会慢慢的揭开这其中的奥秘.

policy 配置文件结构说明

我们看一下默认的file:${java.home}/lib/security/java.policy文件结构是怎么样的:

img_14

而给出对应的关键字解释如下:

  • grant: 用于声明一个权限授予块,定义一组代码需要被赋予的权限。

  • codeBase "值": 指定受权限控制的代码来源(位置)。其中"值"的部分可以是:

    目录/ -> 表示目录下所有 .class 文件, 不包括 .jar 文件.

    目录/* -> 表示目录下所有 .class 文件以及 .jar 文件.

    目录/- -> 表示目录下所有 .class 文件以及 .jar 文件, 包括子目录.

    "值"的部分也可以通过 ${} 来进行引用系统属性.

  • permission:

    java.security.AllPermission表示允许所有权限.

    java.util.PropertyPermission表示键值对Permission.

    java.lang.RuntimePermission表示Runtime使用的Permission.

    ... (后面会对这些 Permission 详细解释)

当然对于文件结构也可以参考官方文档: https://docs.oracle.com/javase/8/docs/technotes/guides/security/PolicyFiles.html

这里的配置原则如下:

  • 没有配置的权限表示没有。
  • 只能配置有什么权限,不能配置禁止做什么。
  • 同一种权限可多次配置,取并集。
  • 统一资源的多种权限可用逗号分割。
配置文件中 grant & codeBase 小实验

如果不做实验, 只是丢一些概念到上面, 那太模糊了, 接下来来做一个小实验, 在SecurityManager开启的情况下进行修改java.version系统变量的值, 我们在file:${java.home}/lib/security/java.policy增加如下声明:

img_15

这里指明了笔者DebuggerDemo编译并运行的路径, 最终由于permission java.security.AllPermission;的定义可以成功修改java.version的值.

Permission 说明【配置文件 & Java 类 都存在】

在上面给出的三个 Permission 中, 我们可以观察它的继承链:

img_16

可以发现无论如何变化, 它们都是实现了java.security.Permission这个类的, 而java.security.Permission这个类是一个以abstract关键字指明的抽象类.

而之所以之前在policy文件中看见很多Permission的参数不一致, 实际上在这里我们也能够找到原因:

img_17

这每个 Permission 定义, 都有不同的含义, Permission | BasicPermission这个抽象类有很多实现, 具体如下:

img_18

所以我们在阅读底层代码时会遇到各种各样不同的 Permission, 实际上它是在policy文件中的定义, 用来比对是否允许操作使用的.

再谈 SecurityManager

在之前初识中我们仅仅只是讲了SecurityManager如何进行设置, 以及设置下来与不设置的区别是什么, 现在我们重新认识以下SecurityManager, 我们要开始理解它的设计大致是用来干嘛的, 首先, SecurityManager中定义了如下方法:

img_19

在这个方法列表中, 我们可以看到几个经典的规律, hasAllPermission方法的定义以及大部分方法都是checkXXX打头.

hasAllPermission & checkXXX 定义

那么我们先来看一下hasAllPermission方法的定义:

img_20

而更加有趣的则是这些checkXXX方法以及hasAllPermission方法都调用了checkPermission方法进行调用, 如图:

img_21

核心都是调用checkPermission方法进行验证权限的, 不过我们先不着急研究checkPermission方法做了什么, 先来自定义一个SecurityManager, 目的其研究它的定位, 是用来干什么的, 随后再进行理解checkPermission方法做了什么.

自定义 SecurityManager【小 WAF 实现】

接下来我们可以实现一个自定义的 SecurityManager 了, 并且我们借用SecurityManager进行实现一个WAF, 禁止调用某方法, 首先我们可以看一下System.exit(0)方法的底层:

img_22

在之前, 我们的SecurityManager是用来判断当前的权限是否在policy配置文件中有定义来实现具体方法的权限判断的, 而如果我们自定义的SecurityManager这么写:

package com.heihu577;
public class Main {
    public static class ExitSecurityManager extends SecurityManager {
        @Override
        public void checkExit(int status) {
            throw new SecurityException("禁止调用 System.exit() 方法");
        }
    }
    public static void main(String[] args) {
        System.setSecurityManager(new ExitSecurityManager());
        System.exit(0);
    }
}

那么就依赖于SecurityManager的机制, 巧妙的利用了SecurityManagercheckXXX方法在函数底层中的判断, 将System.exit()这个方法给 BAN 掉了, 如图:

img_23

访问控制器: AccessController【SecurityManager核心】

checkPermission 方法使用【判断某 Permission 是否在 policy 配置文件中定义】

话说回来, 我们继续研究以下checkPermission方法做了什么:

img_24

方法的定义很简单, 只是间接的调用了java.security.AccessController.checkPermission(传递过来的参数);, 所以重点是java.security.AccessController它做了一些什么事情, 我们没有开启SecurityManager, 可以观察如下结果:

img_25

如果java.policy中定义了permission java.security.AllPermission;, 那么java.security.AccessController.checkPermission(new AllPermission())会返回true.

那么如果没有定义则会抛出异常:

img_26

所以实际上java.security.AccessController.checkPermission方法是用来比对: 当前传入进来的Permission, 在java.policy (系统默认 or 自定义policy)文件中对应的grant (grant codeBase || grant)中是否存在定义, 如果不存在定义则会直接抛出异常. 如果存在定义则静默返回.

而 API 文档 (https://www.apiref.com/java8/java/security/AccessController.html#checkPermission-java.security.Permission-) 对它的介绍如下:

img_27

Permissions::implies 检查是否存在相应的权限

官方 API: https://www.apiref.com/java8/java/security/Permissions.html, 为什么这里突然提到该类, 实际上是因为checkPermission这个方法底层中使用了该类, 并且用它来进行鉴权, 这里简单看一下底层处理机制即可, 首先定义如下java.policy文件内容:

img_28

定义完毕后, 我们DebuggerDemo代码如下, 随后进行 Debug:

public class DebuggerDemo {
    public static void main(String[] args) {
        java.security.AccessController.checkPermission(new PropertyPermission("hack02", "read"));
        System.out.println("代码成功走到该行~");
    }
}

如图:

img_29

对于AccessControlContext的底层处理过程我们不必细究, 只需要理解它们之间的对应关系即可, 最终会调用到ProtectionDomain::implies方法中进行权限查找, 过程如下:

img_30

可以看到的是, 我们的java.policy (默认 policy 文件)成功被解析, 并且马上通过Permissions::implies进行权限判断, 如图:

img_31

Permissions::implies在官方文档中的说明如下:

img_32

doPrivileged 方法使用【借用权限】

以下所有的代码案例测试, 都指明了 -Djava.security.manager, 也就是开启了 SecurityManager.

对于该方法的使用, 我们需要创建如下项目结构:

img_33

WriteFile01 模块测试【允许写操作】
定义 java.policy 权限

随后在java.policy (系统默认)文件中创建如下内容:

grant codeBase "file:///C:/Users/Administrator/Desktop/javaCode/SecurityManager02/WriteFile01/target/classes/" {
       permission  java.io.FilePermission "D:\\test.txt", "write";
};

其目的则是只允许WriteFile01模块进行文件写操作, 不给予WriteFile02文件写操作. 为什么如上这么定义呢?在File::createNewFile方法中可以看到底层校验逻辑:

img_34

所以才定义的permission java.io.FilePermission "D:\\test.txt", "write";.

测试写操作

好, 那么我们话说回来, 在WriteFile01模块中创建如下代码:

package com.heihu577;
import java.io.File;
import java.io.IOException;
import java.security.AccessController;
import java.security.PrivilegedAction;
public class FileUtils {
    public static void main(String[] args) throws IOException {
        new FileUtils().mkFile("D:\\test.txt");
        new FileUtils().mkFile2("D:\\test.txt");
    }
    public void mkFile(String file) throws IOException {
        new File(file).createNewFile();
    }
    public void mkFile2(String file) {
        AccessController.doPrivileged(new PrivilegedAction<Object>() {
            @Override
            public Object run() {
                try {
                    new File(file).createNewFile();
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
                return null;
            }
        });
    }
}

在这里注意一下 mkFile 以及 mkFile2 方法体的定义, mkFile2 是使用了 AccessController.doPrivileged 允许让别的模块调用方法时借用其当前的权限.

其最终结果可以看到的是, 虽然开启了 SecurityManager, 但是由于java.policy中允许对D:\\test.txt进行写操作, 所以最终可以操作成功:

img_35

WriteFile02 模块测试【不允许写操作】

由于我们的java.policy文件中没有定义WriteFile02Permission, 所以我们的WriteFile02模块是不允许写操作的.

在 pom.xml 中引入 WriteFile01

我们可以在WriteFile02中定义pom.xml, 引入WriteFile01, 结果如下:

<dependencies>
    <dependency>
        <groupId>com.heihu577</groupId>
        <artifactId>WriteFile01</artifactId>
        <version>1.0-SNAPSHOT</version>
    </dependency>
</dependencies>
测试写操作【成功与失败案例】

定义如下代码:

package com.heihu577;
import java.io.IOException;
public class WriteFileDemo {
    public static void main(String[] args) throws IOException {
        FileUtils fileUtils = new FileUtils();
        fileUtils.mkFile("D:\\test.txt");
    }
}

最终运行结果:

img_36

最终写入失败!因为我们并没有在java.policy文件中定义WriteFile02模块允许任何操作.

但是我们可以利用WriteFile01模块所定义的mkFile2方法进行操作, 因为WriteFile01 模块的 FileUtils::mkFile2方法体中使用了AccessController.doPrivileged让别的模块借用其权限进行操作, 结果如下:

img_37

这就是AccessController.doPrivileged()方法的作用~

小总结

doPrivileged() 就像是:

  • 你(B 应用)去朋友家(A 应用)做客,朋友(A 应用)临时给你(B 应用)一把钥匙(权限),让你可以打开冰箱拿饮料(原本你没这权限)。
  • 但这把钥匙只能开冰箱,不能开保险柜,而且你离开朋友家后,钥匙就自动失效了。
  • 这样既保证了朋友家的安全,又让你能临时使用必要的资源。

Reference

SecurityManager 入门: https://www.cnblogs.com/yiwangzhibujian/p/6207212.html

SecurityManager 初识: https://blog.spoock.com/2019/12/21/Getting-Started-with-Java-SecurityManager-from-Zero/

SecurityManager 权限校验: https://blog.spoock.com/2019/12/24/Getting-Started-with-Java-SecurityManager-from-Zero-2/

官方 API 文档说明: https://docs.oracle.com/javase/8/docs/api/java/lang/SecurityManager.html & https://docs.oracle.com/javase/7/docs/technotes/guides/security/permissions.html & https://docs.oracle.com/javase/8/docs/technotes/guides/security/PolicyFiles.html

中文 API 文档: https://www.apiref.com/java8/java/security/AccessController.html

官方技术介绍: https://docs.oracle.com/javase/8/docs/technotes/guides/security/spec/security-specTOC.fm.html

简单聊聊 SecurityManager: https://juejin.cn/post/7312818418595004426 & https://juejin.cn/post/6844903657775824910#heading-14

Java 安全之 SecurityManager: https://nicky-chen.github.io/2018/07/13/java-securitymanager/

AccessController doPrivileged 使用: https://blog.csdn.net/xyw591238/article/details/51900275 & https://www.cnblogs.com/liruilong/p/14810513.html

API 部分: https://www.cnblogs.com/qisi/p/security_manager.html

Java 沙箱绕过: https://www.anquanke.com/post/id/151398#h3-5

未经允许不得转载:搜云库 » SecurityManager 权限控制机制详解:Java 安全策略配置与实战案例分析

JetBrains 全家桶,激活、破解、教程

提供 JetBrains 全家桶激活码、注册码、破解补丁下载及详细激活教程,支持 IntelliJ IDEA、PyCharm、WebStorm 等工具的永久激活。无论是破解教程,还是最新激活码,均可免费获得,帮助开发者解决常见激活问题,确保轻松破解并快速使用 JetBrains 软件。获取免费的破解补丁和激活码,快速解决激活难题,全面覆盖 2024/2025 版本!

联系我们联系我们