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

