学习:使用asm,通过挂载javaagent的方式,在启动springboot服务之后,拦截http请求示例

本文详细介绍了如何在Maven项目中创建并配置JavaAgent,包括添加相关依赖,自定义MyJavaAgent、MyInterceptor和MyClassFileTransformer,以及在SpringBoot服务中启用JavaAgent并验证其拦截功能。
摘要由CSDN通过智能技术生成

1. 创建一个javaagent的maven工程

pom文件内容如下

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.log.trace</groupId>
    <artifactId>agent</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>
    <dependencies>

        <dependency>
            <groupId>com.sun</groupId>
            <artifactId>tools</artifactId>
            <version>1.8.0</version>
            <scope>system</scope>
            <systemPath>C:\\Program Files\\Java\\jdk1.8.0_181\\lib\\tools.jar</systemPath>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.ow2.asm/asm -->
        <dependency>
            <groupId>org.ow2.asm</groupId>
            <artifactId>asm</artifactId>
            <version>9.4</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.apache.tomcat.embed/tomcat-embed-core -->
        <dependency>
            <groupId>org.apache.tomcat.embed</groupId>
            <artifactId>tomcat-embed-core</artifactId>
            <version>9.0.85</version>
        </dependency>

    </dependencies>
    <build>
        <plugins>
            <!-- maven 打包集成插件 -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-assembly-plugin</artifactId>
                <version>3.3.0</version>
                <executions>
                    <execution>
                        <phase>package</phase>
                        <goals>
                            <goal>single</goal>
                        </goals>
                    </execution>
                </executions>
                <configuration>
                    <descriptorRefs>
                        <!-- 将依赖一起打包到 JAR -->
                        <descriptorRef>jar-with-dependencies</descriptorRef>
                    </descriptorRefs>
                    <archive>
                        <!--自动添加META-INF/MANIFEST.MF -->
                        <manifest>
                            <addClasspath>true</addClasspath>
                        </manifest>
                        <manifestEntries>
                            <addClasspath>true</addClasspath>
                            <classpathPrefix>lib/</classpathPrefix>
                            <Main-Class>com.log.trace.agent.MyJavaAgent</Main-Class>
                            <Premain-Class>com.log.trace.agent.MyJavaAgent</Premain-Class>
                            <Agent-Class>com.log.trace.agent.MyJavaAgent</Agent-Class>
                            <Can-Redefine-Classes>true</Can-Redefine-Classes>
                            <Can-Retransform-Classes>true</Can-Retransform-Classes>
                        </manifestEntries>
                    </archive>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

2. 创建package

com.log.trace.agent

3. 创建MyAgent.java

package com.log.trace.agent;

import java.lang.instrument.Instrumentation;

public class MyJavaAgent {
    public static void premain(String agentArgs, Instrumentation inst) {
        MyClassFileTransformer transformer = new MyClassFileTransformer();
        inst.addTransformer(transformer, true);
    }
}

4. 创建MyInterceptor.java

自定义拦截逻辑方法beforeRequest

package com.log.trace.agent;


import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;


public class MyInterceptor {
    public static void beforeRequest(Object request, Object response) {
        System.out.println("beforeRequest Intercepting HTTP request headers");
        System.out.println(request);
        System.out.println(response);
        HttpServletRequest httpServletRequest = (HttpServletRequest) request;
        final String requestedId = httpServletRequest.getHeader("X-Requested-ID");
        // 打印客户端提交的X-Requested-ID
        // 当请求报错时,可根据此X-Requested-ID 搜索相关日志
        // 关键词前后300行  grep "X-Requested-ID" test.log -C 300
        // 关键词之后300行  grep "X-Requested-ID" test.log -A 300
        System.out.println("X-Requested-ID ====> " + requestedId);
        HttpServletResponse httpServletResponse = (HttpServletResponse) response;
        // 增加一个特别地响应请求头
        httpServletResponse.addHeader("Interceptor", "Interceptor request");
        httpServletResponse.setContentType("text/plain;charset=UTF-8");
        final PrintWriter writer;
        try {
            writer = httpServletResponse.getWriter();
            writer.write("拦截了的返回");
            writer.flush();
            writer.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

5. 创建MyClassFileTransformer.java

每个http请求都会经过类

org.springframework.web.servlet.DispatcherServlet的doDispatch方法

在doDispatch方法执行之前,执行自定义的拦截逻辑方法beforeRequest

package com.log.trace.agent;

import org.objectweb.asm.*;

import java.lang.instrument.ClassFileTransformer;
import java.security.ProtectionDomain;

public class MyClassFileTransformer implements ClassFileTransformer {
    @Override
    public byte[] transform(
            ClassLoader loader,
            String className,
            Class<?> classBeingRedefined,
            ProtectionDomain protectionDomain,
            byte[] classfileBuffer) {

        if (className.equals("org/springframework/web/servlet/DispatcherServlet")) {
            System.out.println("====> Transforming DispatcherServlet");

            ClassReader cr = new ClassReader(classfileBuffer);
            ClassWriter cw = new ClassWriter(cr, ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES);

            ClassVisitor cv = new ClassVisitor(Opcodes.ASM7, cw) {
                @Override
                public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {
                    MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions);

                    // Inject interceptor logic before doDispatch method in DispatcherServlet
                    if("doDispatch".equals(name)) {
                        return new MethodVisitor(Opcodes.ASM7, mv) {
                            @Override
                            public void visitCode() {
                                try {
                                    // 加载doDispatch方法的第一个参数
                                    // 如果varIndex是0,代表org/springframework/web/servlet/DispatcherServlet那个类的this
                                    mv.visitVarInsn(Opcodes.ALOAD, 1);
                                    mv.visitVarInsn(Opcodes.ALOAD, 2);
                                    // 执行MyInterceptor.beforeRequest 方法
                                    mv.visitMethodInsn(Opcodes.INVOKESTATIC, Type.getInternalName(MyInterceptor.class), "beforeRequest",
                                            Type.getMethodDescriptor(MyInterceptor.class.getMethod("beforeRequest", Object.class, Object.class)), false);
                                } catch (NoSuchMethodException e) {
                                    e.printStackTrace();
                                }
                                super.visitCode();
                            }
                        };
                    }
                    return mv;
                }
            };

            cr.accept(cv, 0);
            return cw.toByteArray();
        }

        return classfileBuffer;
    }
}

6. springboot服务启动参数增加-javaagent

  1. 编译javaagent工程生成javaagent jar包
  2. springboot服务启动参数增加-javaagent参数
-javaagent:C:\Users\test\Documents\java-workspace\log-trace-mock-agent\target\agent-1.0-SNAPSHOT-jar-with-dependencies.jar

7. 向springboot服务发送http请求

拦截成功

响应头有特殊标识

  • 15
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
### 回答1: 可以使用Java Agent,它可以拦截Java程序发出的Http请求,并将其转发到指定的代理服务器。使用Java Agent,可以拦截HttpClient、RestTemplate和HttpURLConnection等客户端发出的Http请求。 ### 回答2: 在Java程序运行时通过Java Agent拦截客户端发出的HTTP请求到指定的代理服务器,步骤如下: 1. 创建一个Java Agent,这个Agent将会在Java程序启动时被加载并运行。Agent可以使用Java的Instrumentation API,来修改已加载类的字节码。 2. 在Java Agent中,使用Instrumentation API找到HTTP客户端类(如HttpClient、RestTemplate和HttpURLConnection)的相关方法。 3. 使用字节码增强技术(如ASMJavassist),修改这些方法的字节码,将它们拦截,并在拦截逻辑中修改HTTP请求的目标地址为代理服务器的地址。 4. 将修改后的字节码重新定义为新的类,并加载到JVM中。 5. Java程序启动时,在运行之前将Java Agent添加到启动参数中,以让Agent被加载。 6. 当HTTP客户端类的方法被调用时,被修改的拦截逻辑将会执行,将HTTP请求发送到指定的代理服务器。 需要注意的是,使用Java Agent拦截HTTP请求需要对字节码修改和类加载机制有一定的了解。此外,代理服务器的配置也需要在程序中进行设置。拦截HTTP请求并将其发送到代理服务器可能会导致性能和安全的考虑,因此在实际应用中需要进行充分的测试和评估。 ### 回答3: 在Java程序运行时,可以通过Java Agent拦截包括HttpClient、RestTemplate、HttpURLConnection等客户端发出的HTTP请求到指定的代理服务器。下面是实现这一功能的步骤: 1. 创建一个Java Agent类,用于拦截HTTP请求。这个类需要继承java.lang.instrument.ClassFileTransformer接口,并实现其中的transform方法。该方法用于对类字节码进行转换。 2. 在transform方法中,使用ASMJava字节码操作框架)来修改目标类的字节码。具体来说,需要定位到HTTP客户端发送请求的方法,比如execute方法。将原来的代码替换为创建代理服务器并发送HTTP请求的代码。 3. 在Java Agent的premain方法中,使用java.lang.instrument.Instrumentation类的addTransformer方法来注册自定义的类转换器。 4. 在程序启动时指定Java Agent,可以通过以下方式之一来实现: * 在启动命令中使用-javaagent参数,如:java -javaagent:/path/to/agent.jar -jar /path/to/program.jar。 * 在程序启动时通过attach API动态加载Java Agent,如使用VirtualMachine类的attach方法和loadAgent方法。 5. 当程序运行时,Java Agent拦截到客户端发送的HTTP请求,并将其重定向到指定的代理服务器。 需要注意的是,由于Java Agent涉及到对字节码进行修改,因此需要了解一些字节码操作的基本知识,并借助ASM等工具来实现字节码的转换。同时,还需要了解HTTP客户端相关的类和方法,在正确的位置进行代码替换。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值