在日常开发实践中,时常需要使用工具(如 Postman、curl命令)来构建http请求进行 开发和测试,当遇到需要token鉴权的接口时,可能需要额外的页面登录或者请求其它接口来获取token,若开发测试过程中需要频繁切换账号时,一直手动获取token就是慢动作了。那么,这个操作是可以优化的吗?
特别注意:这种方法仅能用于测试环境,切勿部署到线上!!!
当构建的http请求指向的是开发测试环境时,不需要手动去获取token,只需提供用户身份标识(如用户名、手机号)即可自动获取并替换token(服务侧执行,不依赖具体使用的请求构建工具)。
为了不修改原有的业务逻辑,所以需要在请求进入 业务代码 前,就把真正可用的token替换到header的token字段中,而兑换token则需要用户标识,所以用户标识也需要传递,由此可以列出几个点:
直接使用原有的token字段,并使用特殊前缀,如username
、phone
为了不关联具体的业务代码,所以token需要在springMVC框架流程中进行替换,通过断点可以容易找到解析header的地方,只要在header报文解析完成之后并且可以获取到header对象的地方进行替换即可。 如:org.apache.coyote.http11.Http11InputBuffer#parseHeaders
这个跟所使用的用户体系强相关的,看提供的是怎样的获取方式,本文的场景是调用dubbo接口即可进行兑换。如:
由上面分析可知,拦截点是org.apache.coyote.http11.Http11InputBuffer#parseHeaders
,只要使用 try finally
块对整个方法的body进行包围,并且把识别和替换token的逻辑放在 finally
块中即可。
Tips:将token相关操作都封装到一个静态方法里边,编写插桩逻辑的时候会方便很多!
由上边分析可知,token的兑换需要调用dubbo接口,但是我们选择的token拦截替换点
并不是一个bean的方法,也没有dubbo接口的上下文,只是一个实例方法,那么要怎么调用dubbo接口呢?
比较麻烦,而且也需要读取相应的配置,而且会创建额外的dubbo接口对象,不予采用
通过跟踪@Reference
注解的处理过程可以发现,所有动态生成的dubbo接口代理类都会存放在 com.alibaba.dubbo.config.spring.AnnotationBean
中的referenceConfigs
字段中:
并且com.alibaba.dubbo.config.spring.AnnotationBean
是一个一个bean!!!
只要我们能获取到该bean,获取bean字段中的dubbo代理类还不手到擒来!
但是,拦截点处也没有bean的上下文呀!
这个比较简单,只要在代码中获取到 ApplicationContext
,并且赋值给一个静态字段即可,
如下:利用 org.springframework.context.ApplicationContextAware
然后再使用 spring.factories
让 @Component
生效,如下:
获取到bean后,还需要获取到dubbo为@Reference
生成的实例才行
源码可见com.alibaba.dubbo.config.spring.AnnotationBean
中的referenceConfigs
字段是private
的,所以要用下反射,如下:
dubbo接口已经准备好了,还需要加上具体的处理逻辑
这一步需要拦截特定格式的token,取出身份标识并调用dubbo兑换token,然后替换掉原来在header中的token即可
经过上边的努力,就可以进行javaagent打包来运行了!
Tips:打包javaagent可以使用 jar-with-dependencies 把依赖一起打包进去,并且当javaagent中的类需要依赖目标应用中的类或依赖时,其pom的scope需要声明为 provided,不然会把这部分依赖也打包进去
在 Idea run config 中的vmoption加上javaagent参数
-javaagent:/path/to/agent/intercept-token-1.0-SNAPSHOT-jar-with-dependencies.jar
复制代码
perfect!!!
Tips:如果你不使用
jar-in-jar/nested-jars
(使用springboot的jar打包插件)的方式部署项目,那么到这里已经可以了~
既然 idea 跑成功了,那就部署 测试环境(打包镜像并部署到容器中,用的是springboot的jar打包插件,将逻辑和依赖打包到一个jar包)中试试吧!
/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
复制代码
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 ......
复制代码
why?
为什么会报这个错误呢?
为什么直接idea运行没有问题,为什么使用springboot插件的打包方式运行就报错了呢?
详情请见下回分解->Springboot上运行javaagent时出现NoClassDefFoundError错误的分析和解决
如果你觉得此文对你有一丁点帮助,点个赞。或者可以加入我的开发交流群:1025263163相互学习,我们会有专业的技术答疑解惑
如果你觉得这篇文章对你有点用的话,麻烦请给我们的开源项目点点star:http://github.crmeb.net/u/defu不胜感激 !
PHP学习手册:https://doc.crmeb.com
技术交流论坛:https://q.crmeb.com
|