JavaFx的WebView请求头accept-language、accept-encoding、accept-charset这三个是无法更改的且不能添加自定义头信息,官方提供的接口只有更改UA。
JavaFx每次网络请求都会访问 prepreareConnection 方法,这样我们可用利用动态字节码技术在方法内部添加一些我们自己的代码,可以看到下方的源码,官方是通过 c.setRequestProperty 方法设置请求头的。
现在我们开始写代码,实现自定义头信息和覆盖官方头信息。
需要的jar包:ea-agent-loader、javassist
<dependency>
<groupId>com.ea.agentloader</groupId>
<artifactId>ea-agent-loader</artifactId>
<version>1.0.3</version>
</dependency>
<dependency>
<groupId>org.javassist</groupId>
<artifactId>javassist</artifactId>
<version>3.26.0-GA</version>
</dependency>
MyJavaAgent.java
import java.lang.instrument.Instrumentation;
public class MyJavaAgent {
public static void agentmain(String agentArgument, Instrumentation instrumentation) {
ClassTransformer transformer = new ClassTransformer();
instrumentation.addTransformer(transformer);
}
}
ClassTransformer.java
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;
import java.io.ByteArrayInputStream;
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
import java.security.ProtectionDomain;
public class ClassTransformer implements ClassFileTransformer {
@Override
public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
byte[] byteCode = classfileBuffer;
// javaFx webview 请求是通过这个类进行加载的
if ("com/sun/webkit/network/URLLoader".equals(className)) {
try {
ClassPool classPool = new ClassPool(true);
CtClass ctClass = classPool.makeClass(new ByteArrayInputStream(classfileBuffer));
// 要修改的方法,因为设置请求头是在这个方法里面设置的
CtMethod method = ctClass.getDeclaredMethod("prepareConnection");
StringBuilder sb = new StringBuilder();
// 注入代码 $1 表示此方法的第一个参数
sb.append("$1.setRequestProperty(\"Referer\", \"https://www.baidu.com/\");");
// 在代码最前面注入代码
method.insertBefore(sb.toString());
byteCode = ctClass.toBytecode();
ctClass.detach();
} catch (Exception e) {
e.printStackTrace();
}
}
return byteCode;
}
}
Main.java
public static void main(String[] args) {
// 在main方法的第一行执行
AgentLoader.loadAgentClass(MyJavaAgent.class.getName(), null);
launch(args);
}
这时候,我们启动程序来看下效果。
可以看到,我们已经成功注入的自定义头信息,那如何覆盖官方的accept-language、accept-encoding呢?很简单只需要改动 ClassTransformer.java transform 方法里面的 method.insertBefore 改成 method.insertAfter 即可覆盖