java 17起securitymanager被弃用,java 21中彻底移除,不建议新项目使用;其通过jvm自动调用checkxxx方法实现权限拦截,配合policy文件可声明式授权;现代替代方案包括模块系统、声明式权限、os级沙箱及字节码增强。

Java 中的 SecurityManager 和 SecurityException 曾是实现细粒度运行时权限控制的核心机制,但需明确:自 Java 17 起,SecurityManager 已被标记为 deprecated,并在 Java 21 中被彻底移除。因此,**不建议在新项目中依赖它实现资源拦截**。不过,理解其原理对维护遗留系统或深入 JVM 安全模型仍有价值。
SecurityManager 的工作原理与拦截逻辑
SecurityManager 是一个全局可设置的策略检查器,JVM 在执行敏感操作(如文件读写、网络连接、反射调用)前,会自动调用其对应检查方法(如 checkRead()、checkConnect())。若当前执行上下文未被授予相应权限,它就抛出 SecurityException,从而中断操作。
关键点在于:拦截不是靠开发者手动 throw,而是由 JVM 主动触发检查 —— 你只需重写检查方法并按需抛异常。
典型拦截示例:限制文件系统访问
以下是一个自定义 SecurityManager 的简化实现,用于禁止读取 /etc/passwd 和所有 .class 文件:
public class RestrictedSecurityManager extends SecurityManager {
@Override
public void checkRead(String file) {
if ("/etc/passwd".equals(file) || file.endsWith(".class")) {
throw new SecurityException("Access denied: reading " + file);
}
}
@Override
public void checkWrite(String file) {
if (file.contains("/tmp/secret")) {
throw new SecurityException("Write to /tmp/secret is forbidden");
}
}
}
启用方式(仅限 Java 8–16):
- 启动时添加 JVM 参数:
-Djava.security.manager=RestrictedSecurityManager - 或代码中动态设置(需在应用早期、任何敏感操作前):
System.setSecurityManager(new RestrictedSecurityManager());
权限策略文件(policy file)配合使用
单纯重写 SecurityManager 不够灵活。实际生产中通常结合 java.policy 文件声明哪些代码能做什么:
grant codeBase "file:/app/trusted.jar" {
permission java.io.FilePermission "/tmp/-", "read,write";
permission java.net.SocketPermission "example.com:80", "connect";
};
grant {
// 默认拒绝所有,强制走自定义 checkXXX 方法
};
这样,只有显式授权的代码才能绕过你的拦截逻辑;其余代码一律触发你重写的检查方法。
现代替代方案(必须了解)
由于 SecurityManager 已废弃,当前推荐的安全控制方式包括:
-
模块系统(Java 9+):用
module-info.java控制包导出与服务依赖,从编译期隔离敏感 API -
运行时权限模型(Android / Jakarta EE):基于角色或能力的声明式权限(如
@RolesAllowed) - 沙箱容器(Docker + seccomp/bpf):在 OS 层拦截系统调用,比 JVM 层更底层、更可靠
- 字节码增强(Byte Buddy / ASM):在类加载时注入访问检查逻辑,适用于需要精细控制的老系统改造
对于新系统,优先考虑最小权限原则 + 外部沙箱 + 明确的权限边界设计,而非依赖 JVM 内置的 SecurityManager。










