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

基于ByteBuddy实现特定包类文件读取权限控制的问题

问题背景

我需要实现仅允许特定包下的类读取指定目录中的文件。之前可以通过SecurityManager的授权策略文件实现,示例配置如下:

grant codeBase "file:/path/to/application.jar" {
    permission java.io.FilePermission "/path/to/directory/*", "read,write";
    permission java.lang.RuntimePermission "accessClassInPackage.allowed.package";
};

但SecurityManager已被废弃,希望通过ByteBuddy实现相同功能。


Edit 1:首次尝试未触发拦截

我编写了FileInterceptor类拦截文件读取操作,在BufferedReader层面实现拦截,代码如下:

public class FileInterceptor {

    public static void premain(String agentArgs, Instrumentation instrumentation) throws IOException {

        System.out.println("Inside premain");
        File temp = Files.createTempDirectory("tmp").toFile();
        Map<TypeDescription.ForLoadedType, byte[]> injectTypes = new HashMap<>();
        injectTypes.put(new TypeDescription.ForLoadedType(BufferedReaderAdvice.class),
            ClassFileLocator.ForClassLoader.read(BufferedReaderAdvice.class));
        ClassInjector.UsingInstrumentation.of(temp,
            ClassInjector.UsingInstrumentation.Target.BOOTSTRAP, instrumentation).inject(injectTypes);

        new AgentBuilder.Default()
            .ignore(ElementMatchers.nameStartsWith("net.bytebuddy."))
            .with(new AgentBuilder.InjectionStrategy.UsingInstrumentation(instrumentation, temp))
            .type(ElementMatchers.named("java.io.BufferedReader"))
            .transform((builder, typeDescription, classLoader, module, protectionDomain) ->
                builder.visit(Advice.to(BufferedReaderAdvice.class).on(
                    ElementMatchers.isConstructor().and(ElementMatchers.any()
                    )))) // Intercept calls to BufferedReader(InputStreamReader)
            .installOn(instrumentation);
    }

    public static class BufferedReaderAdvice {
        @Advice.OnMethodEnter()
        
        public static void enter(@Advice.This BufferedReader bufferedReader, @Advice.Argument(0) Reader fileReader) {
            String path = new File(((InputStreamReader) fileReader).getEncoding()).getAbsolutePath(); // Get the absolute path of the file
            System.out.println("Inside enter");
            if (path.startsWith("/path/") &&
                !bufferedReader.getClass().getPackage().getName().startsWith("com.whitelist")) {
                // Deny access for files in the path of interest but not being accessed by a package of interest
                throw new SecurityException("Access denied");
            }
        }
    }

}

将该类作为JavaAgent在另一个项目中使用,调用Files.readAllLines(path)时,仅能看到"Inside premain"输出,但拦截器的enter方法并未触发,请问遗漏了什么?


Edit 2:调整后仍未触发拦截

调整了premain方法和Advice代码,如下:

premain方法

public static void premain(String agentArgs, Instrumentation instrumentation) throws IOException {

        System.out.println("Inside premain");
        new AgentBuilder.Default().disableClassFormatChanges().with(RETRANSFORMATION).ignore(none())
            .type(ElementMatchers.named("java.io.BufferedReader"))
            .transform((builder, typeDescription, classLoader, module, protectionDomain) ->
                builder.visit(Advice.to(BufferedReaderAdvice.class).on(isConstructor())))
            .installOn(instrumentation);
}

BufferedReaderAdvice类

public static class BufferedReaderAdvice {
        @Advice.OnMethodEnter()
        public static void enter(@Advice.Origin Constructor constructor, @Advice.AllArguments Object[] params) {
            System.out.println("Inside enter");
            System.out.println("enter-------" + constructor.getDeclaringClass());
            String callingPackage = constructor.getDeclaringClass().getPackage().getName();
            InputStreamReader streamReader = (InputStreamReader) params[0];
            String path = new File(streamReader.getEncoding()).getAbsolutePath(); // Get the absolute path of the file
            System.out.println("callingPackage-->" + callingPackage);
            System.out.println("path-->" + path);
            for (Object param:params) {
                System.out.println(param);
            }
        }
    }

调用代码

public void readFile(String filePath) {
        System.out.println("Class:"+getClass().getProtectionDomain().getCodeSource().getLocation());
        Path path = Paths.get(filePath);
        try {
            List<String> lines = Files.readAllLines(path);
            System.out.println(lines);
        } catch (IOException ex) {
            // handle exception...
        }
    }

调整后仍未触发enter方法,请求排查问题。


排查要点
  1. 目标类加载时机问题:java.io.BufferedReader属于引导类加载器加载的核心类,可能在JavaAgent的premain执行前就已加载。即使添加了with(RETRANSFORMATION),也需确认JVM环境是否允许类重转换(默认允许,部分受限环境可能例外)。
  2. 拦截点匹配偏差:Files.readAllLines底层未必直接调用你拦截的BufferedReader构造器重载。比如它可能使用FileReader或其他Reader实现,甚至直接包装InputStream。建议先梳理Files.readAllLines的调用链,找到真正被触发的文件读取相关方法。
  3. Advice类的类加载器访问性:BufferedReader由引导类加载器加载,你的Advice类需要能被引导类加载器访问才能注入成功。Edit1中的类注入逻辑被移除后,可重新添加该逻辑,或通过-Xbootclasspath/a:参数将Advice所在Jar包加入引导类路径。
  4. 参数类型转换风险:Edit2的Advice中直接将params[0]强转为InputStreamReader,如果构造器传入的是其他类型,会抛出异常导致拦截逻辑静默失败(ByteBuddy可能处理了异常,你看不到输出)。建议先打印params[0]的实际类型,再做类型转换。
  5. 版本兼容性问题:确保ByteBuddy版本与当前JVM版本匹配,比如JDK11+需要使用适配的ByteBuddy版本,避免API不兼容导致注入失败。
  6. 添加ByteBuddy日志排查:在AgentBuilder中添加with(AgentBuilder.Listener.StreamWriting.toSystemOut()),查看ByteBuddy的转换日志,确认BufferedReader是否被成功转换。

内容的提问来源于stack exchange,提问作者Sagar

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.02 03:35:06