System.getSecurityManager() 在 JDK 21 中已被彻底移除,因其性能差、难调试、不兼容模块系统和现代 JVM;应改用 AccessController.checkPermission() 结合策略文件、最小权限部署及框架级防护实现安全控制。

Java 的 System.getSecurityManager() 已在 JDK 17 中被标记为废弃(deprecated for removal),并在 JDK 21 中**默认禁用且无法启用**。因此,**不推荐、也不应再依赖它来实现权限检查**。
为什么不能继续用 SecurityManager 做权限控制
SecurityManager 是 Java 早期(JDK 1.0)设计的安全机制,基于栈遍历(stack walking)动态检查调用链权限,存在严重性能开销、难以调试、与模块系统(JPMS)和现代 JVM 特性(如 GraalVM、JLink、封闭模块)不兼容等问题。Oracle 明确将其移除路线图写入 JEP 411,并已在 JDK 21 彻底删除其运行时支持。
替代方案:使用标准的 Java 权限模型(基于 AccessController + Policy)
虽然 SecurityManager 被移除,但底层权限抽象(java.security.Permission 及其子类)仍保留。现代做法是:显式声明所需权限 + 在关键操作前手动调用 AccessController.checkPermission(),由 JVM 根据当前 Policy 配置决定是否授权。
- 定义权限需求:例如读文件需
new FilePermission("/tmp/data.txt", "read") - 执行检查(无需 SecurityManager 运行):
AccessController.checkPermission(new FilePermission("/tmp/data.txt", "read")); - 该调用会触发 JVM 查询当前
Policy实例(默认为Policy.getPolicy()返回的对象),判断当前执行上下文是否有对应权限 - 若无权限,抛出
AccessControlException
更推荐的现代实践:职责分离 + 显式授权 + 框架级防护
面向生产环境,应避免运行时动态权限检查,转而采用以下组合策略:
-
启动时验证权限:应用初始化阶段调用
AccessController.checkPermission()验证必需权限(如网络、文件、反射),失败则快速退出并提示明确错误 -
最小权限原则部署:通过 JVM 参数
-Djava.security.policy=app.policy指定精简策略文件,只授予必要权限;配合容器/OS 级限制(如 Linux capabilities、seccomp、只读文件系统) - 框架/库内置防护:Spring Security、Micrometer、Log4j2 等主流库已不再依赖 SecurityManager,而是提供配置化访问控制(如 @PreAuthorize、filter chain、log level 级别限制)
- 敏感操作封装与审计日志:将文件读写、系统命令执行、反射调用等封装为受控服务,统一记录操作者、时间、参数,并集成到审计系统
如果你仍在维护旧版 JDK(≤16)项目
可临时使用 SecurityManager,但必须注意:
- 启动 JVM 时需显式启用:
java -Djava.security.manager=allow ... - 自定义
SecurityManager子类重写checkPermission(Permission),避免直接调用父类导致栈遍历开销 - 检查前确保已安装:
if (System.getSecurityManager() != null) { ... },否则空指针 - 示例:
SecurityManager sm = System.getSecurityManager(); if (sm != null) { sm.checkPermission(new RuntimePermission("createClassLoader")); }
不复杂但容易忽略:真正安全的系统不是靠运行时拦截,而是靠设计时约束、部署时隔离、运行时监控三者结合。SecurityManager 已成历史,转向声明式策略、最小权限和纵深防御才是正解。










