如何通过Java SecurityManager确保方法仅被指定方法调用?
实现B类getSensitiveData()仅允许A类私有方法调用的权限控制方案
核心思路
我们通过自定义专属权限,让A类的私有方法在调用B的敏感数据方法时携带这个权限,同时让B的方法在执行前检查当前调用上下文是否拥有该权限。这种方式不依赖类名等易变信息,即使代码被混淆,权限控制逻辑依然有效。
完整可运行代码
A类实现
import java.security.AccessControlContext; import java.security.AccessController; import java.security.Permission; import java.security.PermissionCollection; import java.security.PrivilegedAction; import java.security.ProtectionDomain; import java.util.Enumeration; public final class A { private transient final B sensitiveHolder; public A(B sensitiveHolder) { this.sensitiveHolder = sensitiveHolder; } private final byte[] getSensitiveData() { // 定义专属权限,用于标识合法调用者 RuntimePermission sensitiveAccessPerm = new RuntimePermission("SensitiveDataAccess"); // 创建仅包含该权限的权限集合 PermissionCollection permCollection = new PermissionCollection() { @Override public void add(Permission permission) { if (permission.equals(sensitiveAccessPerm)) { super.add(permission); } } @Override public boolean implies(Permission permission) { return permission.getName().equals("SensitiveDataAccess"); } @Override public Enumeration<Permission> elements() { return super.elements(); } }; permCollection.add(sensitiveAccessPerm); // 构造携带专属权限的特权上下文 AccessControlContext acc = new AccessControlContext( new ProtectionDomain[]{new ProtectionDomain(null, permCollection)}); // 在特权块内调用B的方法,此时调用上下文会带上专属权限 return AccessController.doPrivileged((PrivilegedAction<byte[]>) () -> sensitiveHolder.getSensitiveData(), acc); } }
B类实现
import java.security.SecurityManager; import java.security.RuntimePermission; public final class B { private transient final byte[] sensitiveData; // 构造时传入加密后的敏感数据(实际场景应为加密后的密钥/身份证等) public B(byte[] encryptedSensitiveData) { this.sensitiveData = encryptedSensitiveData; } public final byte[] getSensitiveData() { SecurityManager securityManager = System.getSecurityManager(); if (securityManager != null) { // 检查当前调用上下文是否拥有专属权限 securityManager.checkPermission(new RuntimePermission("SensitiveDataAccess")); } // 模拟解密逻辑:实际场景替换为用DEK解密敏感数据的真实代码 return decryptSensitiveData(sensitiveData); } // 模拟解密方法 private byte[] decryptSensitiveData(byte[] encryptedData) { // 此处仅做示例,实际需实现对应解密逻辑 return encryptedData; } }
测试代码
import java.lang.reflect.Method; public class Test { public static void main(String[] args) { // 注意:Java 9+ 需要通过启动参数启用SecurityManager:java -Djava.security.manager Test // Java 8及之前默认可能已启用,若未启用也需添加该参数 // 初始化加密后的敏感数据 byte[] encryptedData = "encrypted-id-123456789".getBytes(); B sensitiveHolder = new B(encryptedData); A aInstance = new A(sensitiveHolder); // 测试1:直接调用B的getSensitiveData(),应该被权限控制拒绝 try { sensitiveHolder.getSensitiveData(); System.out.println("错误:直接调用B的方法成功了!"); } catch (SecurityException e) { System.out.println("正确:直接调用B的方法被拒绝,异常信息:" + e.getMessage()); } // 测试2:通过A的私有方法调用,应该成功获取敏感数据 try { // 因A的getSensitiveData是私有方法,用反射模拟调用(实际场景中A内部会有合法调用路径) Method getSensitiveMethod = A.class.getDeclaredMethod("getSensitiveData"); getSensitiveMethod.setAccessible(true); byte[] plainData = (byte[]) getSensitiveMethod.invoke(aInstance); System.out.println("正确:通过A的私有方法获取到敏感数据:" + new String(plainData)); } catch (Exception e) { System.err.println("调用A的私有方法失败:"); e.printStackTrace(); } } }
关键说明
- 自定义权限:使用
"SensitiveDataAccess"专属权限标识合法调用,避免依赖类名(代码混淆后类名会变化,用权限更可靠)。 - 特权上下文:A的私有方法通过
AccessController.doPrivileged创建携带专属权限的上下文,确保调用B方法时上下文包含合法权限。 - 权限校验:B的方法通过
SecurityManager.checkPermission验证调用上下文权限,只有A的特权调用能通过,其他调用会抛出SecurityException。 - 兼容性:Java 9+中SecurityManager被标记为废弃,但如果项目需要使用该机制,仍可通过启动参数启用。
内容的提问来源于stack exchange,提问作者theAnonymous
相关产品推荐
相关产品推荐

