You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何通过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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.28 06:27:41