SecurityManager 是 Java 运行时权限检查中枢,但自 Java 17 起被标记为废弃(@Deprecated(forRemoval=true)),Java 21 正式移除,新项目中已不可用,被模块化访问控制和容器隔离等机制替代。

SecurityManager 是什么,现在还用吗
Java 的 SecurityManager 是一个运行时权限检查中枢,它会在代码尝试读文件、连网络、改系统属性等敏感操作前强制拦截并校验权限。但自 Java 17 起,SecurityManager 已被标记为 @Deprecated(forRemoval=true);Java 21 正式移除——这意味着你用 -Djava.security.manager 启动会直接报错 Unrecognized VM option。
- 它不是“废弃功能”,而是被更底层、更细粒度的模块化访问控制(如
java.base模块的opens/exports策略)和容器级隔离(如 Linux namespace/cgroups)逐步替代 - 如果你在维护老项目(尤其是 Java 8/11 的 Applet 或 Web Start 应用),它仍可能生效;但新项目中强行启用只会失败
- 别试图用反射绕过:JVM 启动后禁止动态安装
SecurityManager,System.setSecurityManager()在 Java 17+ 会抛UnsupportedOperationException
为什么早期要用 SecurityManager 做沙箱
因为 JVM 需要区分“谁写的代码”和“能干啥”。比如浏览器里加载的 Applet 是从远端服务器下载的字节码,完全不可信,必须限制它连本地 /etc/passwd、调用 Runtime.exec("rm -rf /") 或监听 localhost:8080。
- 类加载器按来源划分命名空间:
URLClassLoader加载的远程类 vsAppClassLoader加载的本地类,天然隔离 -
SecurityManager结合Policy文件(如java.policy)做运行时决策:某段代码是否拥有FilePermission "read"权限 - 所有 JDK 核心 IO 类(
File.exists()、Socket.connect()、System.getenv("PATH"))内部都埋了checkRead()/checkConnect()/checkPropertyAccess()调用
不用 SecurityManager 了,那现在怎么实现沙箱
现代 Java 不再靠单一管理器,而是分层设防:编译期约束 + 启动时隔离 + 运行时委托。
- 模块系统(Java 9+):用
module-info.java显式声明requires和opens,未开放的包外部无法反射访问 - JVM 参数替代方案:
--add-opens java.base/java.lang=ALL-UNNAMED控制反射权限,比全局SecurityManager更精准 - 容器/沙箱环境:把 Java 进程跑在 gVisor、Firecracker 或 Docker 中,由 OS 层限制
openat()、bind()等系统调用 - 第三方库兜底:如
io.github.classgraph做类路径扫描时可配置白名单;com.fasterxml.jackson反序列化默认禁用DefaultTyping防 gadget chain
踩坑最多的三个地方
很多团队在迁移或排查问题时,卡在“以为还在起作用”的幻觉里。
立即学习“Java免费学习笔记(深入)”;
- 误以为
System.getSecurityManager() != null就代表沙箱生效 —— 实际上 Java 17+ 默认返回null,且无法再设置 - 在 Spring Boot 应用里写
System.setSecurityManager(new SecurityManager()),结果启动直接失败,错误是java.lang.UnsupportedOperationException: The Security Manager is no longer supported - 依赖旧文档配置
java.security.policy文件却没配-Djava.security.manager,结果策略完全不加载,而你还在奇怪“为什么文件读取没被拦住”
真正需要沙箱逻辑的场景(比如插件平台),现在得自己基于 ClassLoader + AccessController.doPrivileged + 模块导出规则组合实现,没法靠一个开关打开。










