1, Scene source
In daily development practice, it is often necessary to use tools (such as Postman and curl commands) to build http requests for development and testing. When an interface requiring token authentication is encountered, it may be necessary to log in to additional pages or request other interfaces to obtain tokens. If it is necessary to switch accounts frequently during development and testing, it is a slow action to obtain tokens manually all the time. So, can this operation be optimized?
- Project environment: springboot + web + dubbo
- Request hint: put the token in the token field of the header
Special note: this method can only be used in the test environment and should not be deployed online!!!
When the constructed http request points to the development and test environment, you do not need to obtain the token manually. You only need to provide the user ID (such as user name and mobile phone number) to automatically obtain and replace the token (executed on the service side, independent of the specific request construction tool).
- No trace can be left on the code submission record
- It needs to be compatible with the old logic and does not affect the normal function
- It does not rely on specific business logic and is as common as possible
4, Implementation scheme
1. Brief link of the request
In order not to modify the original business logic, it is necessary to replace the really available token into the token field of the header before the request enters the business code, and the user ID is required to exchange the token, so the user ID also needs to be passed. Therefore, several points can be listed:
2. Where did the user ID come from?
Directly use the original token field and use special prefixes, such as username and phone
3. Where to replace the token?
In order not to associate with the specific business code, the token needs to be replaced in the spring MVC framework process. The place where the header can be parsed can be easily found through the breakpoint, as long as the place where the header object can be obtained after the header message is parsed. For example: org apache. coyote. http11. Http11InputBuffer#parseHeaders
4. How to use user ID to exchange token?
This is strongly related to the user system used. It depends on the acquisition method provided. The scenario in this article is to call the dubbo interface to exchange. For example:
5. Request link after adding interception point
5, Implementation steps
1. Implementation of token interception point:
According to the above analysis, the interception point is org apache. coyote. http11. Http11inputbuffer #parseheaders, just use the try finally block to surround the body of the whole method, and put the logic of identifying and replacing token s in the finally block.
- javaassist implementation:
Tips: encapsulating token related operations into a static method will be much more convenient when writing pile insertion logic!
It can be seen from the above analysis that the token exchange needs to call the dubbo interface, but the token interception and replacement point we selected is not a bean method, nor the context of the dubbo interface, but an instance method. So how do we call the dubbo interface?
a. Generalization call [not used]
It is troublesome, and the corresponding configuration needs to be read, and additional dubbo interface objects will be created, which will not be used
b. Expose the dubbo instance to static [just you]
By tracking @Reference In the process of annotation processing, it can be found that all dynamically generated Dubbo interface proxy classes will be stored in com alibaba. dubbo. config. spring. In the referenceconfigures field in the annotationbean:
And com alibaba. dubbo. config. spring. Annotationbean is a bean!!!
As long as we can get the bean, it's not easy to get the dubbo proxy class in the bean field!
However, there is no bean context at the interception point!
1. Expose bean s to static
This is relatively simple. Just get the ApplicationContext in the code and assign it to a static field,
As follows: use org springframework. context. ApplicationContextAware Then use spring Factories let @Component Effective as follows:
After obtaining the bean, you also need to obtain dubbo as @Reference The generated instance is OK
II. Get dubbo instance from bean
The source code can be found in com alibaba. dubbo. config. spring. The referenceconfigures field in the annotationbean is private, so the following reflection is required:
The dubbo interface is ready, and specific processing logic needs to be added
3. Interception processing logic
In this step, you need to intercept the token in a specific format, take out the identity, call dubbo to exchange the token, and then replace the token in the header
After the above efforts, you can package Java agent to run!
Tips: jar with dependencies can be used to package the dependencies of javaagent. When the classes in javaagent need to depend on the classes or dependencies in the target application, the scope of its pom needs to be declared as provided, otherwise this part of the dependencies will also be packaged
- Configure javaagent:
Add the javaagent parameter to vmoption in Idea run config
-javaagent:/path/to/agent/intercept-token-1.0-SNAPSHOT-jar-with-dependencies.jar Copy code
- Arthas looks at the following pile insertion:
Tips: if you don't use jar in jar / nested jars (the jar packaging plug-in of spring boot) to deploy the project, you can do it here
2. Deploy to the test environment and try to run [fail]
Now that idea runs successfully, try deploying the test environment (packaging the image and deploying it into the container, using the jar packaging plug-in of springboot, and packaging the logic and dependencies into a jar package)!
- Examples of startup parameters:
/Library/Java/JavaVirtualMachines/jdk1.8.0_251.jdk/Contents/Home/bin/java \ -Dserver.port=9060 \ -javaagent:/path/to/agent/intercept-token-1.0-SNAPSHOT-jar-with-dependencies.jar \ -jar \ /path/to/springboot-application.jar Copy code
- report errors:
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'methodValidationPostProcessor' defined in class path resource [org/springframework/boot/autoconfigure/validation/ValidationAutoConfiguration.class]: Unsatisfied dependency expressed through method 'methodValidationPostProcessor' parameter 0; nested exception is org.springframework.beans.factory.CannotLoadBeanClassException: Error loading class [com.wingli.agent.helper.util.SpringContextHolder] for bean with name 'com.wingli.agent.helper.util.SpringContextHolder': problem with class file or dependent class; nested exception is java.lang.NoClassDefFoundError: org/springframework/context/ApplicationContextAware at ...... Caused by: org.springframework.beans.factory.CannotLoadBeanClassException: Error loading class [com.wingli.agent.helper.util.SpringContextHolder] for bean with name 'com.wingli.agent.helper.util.SpringContextHolder': problem with class file or dependent class; nested exception is java.lang.NoClassDefFoundError: org/springframework/context/ApplicationContextAware at ...... Caused by: java.lang.ClassNotFoundException: org.springframework.context.ApplicationContextAware at java.net.URLClassLoader.findClass(URLClassLoader.java:382) ~[?:1.8.0_251] at java.lang.ClassLoader.loadClass(ClassLoader.java:418) ~[?:1.8.0_251] at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:355) ~[?:1.8.0_251] at java.lang.ClassLoader.loadClass(ClassLoader.java:351) ~[?:1.8.0_251] at java.lang.ClassLoader.defineClass1(Native Method) ~[?:1.8.0_251] at java.lang.ClassLoader.defineClass(ClassLoader.java:756) ~[?:1.8.0_251] at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142) ~[?:1.8.0_251] at java.net.URLClassLoader.defineClass(URLClassLoader.java:468) ~[?:1.8.0_251] at java.net.URLClassLoader.access$100(URLClassLoader.java:74) ~[?:1.8.0_251] at java.net.URLClassLoader$1.run(URLClassLoader.java:369) ~[?:1.8.0_251] at java.net.URLClassLoader$1.run(URLClassLoader.java:363) ~[?:1.8.0_251] at java.security.AccessController.doPrivileged(Native Method) ~[?:1.8.0_251] at java.net.URLClassLoader.findClass(URLClassLoader.java:362) ~[?:1.8.0_251] at java.lang.ClassLoader.loadClass(ClassLoader.java:418) ~[?:1.8.0_251] at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:355) ~[?:1.8.0_251] at java.lang.ClassLoader.loadClass(ClassLoader.java:405) ~[?:1.8.0_251] at org.springframework.boot.loader.LaunchedURLClassLoader.loadClass(LaunchedURLClassLoader.java:94) ~[study-minder.jar:?] at java.lang.ClassLoader.loadClass(ClassLoader.java:351) ~[?:1.8.0_251] at org.springframework.util.ClassUtils.forName(ClassUtils.java:251) ~[spring-core-4.3.20.RELEASE.jar!/:4.3.20.RELEASE] at ...... Copy code
Why did you report this error?
Why is there no problem running idea directly? Why is it wrong to use the packaging method of spring boot plug-in?
For details, please refer to the analysis and solution of NoClassDefFoundError error when running javaagent on springboot in the next section
If you think this article is a little helpful to you, give it a compliment. Or you can join my development exchange group: 1025263163 learn from each other, and we will have professional technical Q & A to solve doubts
If you think this article is useful to you, please click star: http://github.crmeb.net/u/defu Thank you very much!