Intercept and replace token s in springboot to simplify authentication

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!!!

2, Expectation
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).

3, Limit
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 the user ID to exchange token s?
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.

Schematic:

javaassist implementation:

Tips: encapsulating token related operations into a static method will be much more convenient when writing pile insertion logic!

2.token exchange:
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 tracing the processing of @ Reference annotation, 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 make @ Component effective as follows:

After obtaining the bean, you need to obtain the instance generated by dubbo for @ Reference

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

6, Running
The javaagent can be packaged and run after efforts!

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

1.idea run[success]
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:

perfect!!!

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?
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

last
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 esteem it a favor!

PHP learning manual: https://doc.crmeb.com
Technical exchange forum: https://q.crmeb.com

Keywords: Java Spring Boot PostMan

Added by mouloud2001 on Wed, 23 Feb 2022 05:16:59 +0200