来源:https://zhenbianshu.github.io/文章介绍了spring-boot中实现generalauth的四种方式,包括传统的AOP、拦截器、参数解析器和过滤器,并提供了相应的示例代码,最后是一个他们的执行顺序的简要总结。前言最近被无穷无尽的业务需求压得喘不过气来。我终于得到了一份让我突破代码舒适区的工作。解决的过程非常曲折。这让我一度怀疑人生,但收获也很大。从代码上看并不明显,但感觉已经揭开了java、Tomcat、Spring一直遮住眼睛的那层面纱。对它们的理解达到了一个新的水平。好久没有输出了,就挑一个方面来总结一下,希望在整理的过程中能学到一些其他的东西。由于Java生态的繁荣,以下每个模块都有大量的文章专门介绍。所以我选择了另外一个角度,从实际问题出发,把这些零散的知识联系起来,大家可以看成一个总结。对于每个模块的最终详细介绍,可以去官方文档或者阅读网上其他的博客。需求简单明了,完全不像产品提出的风骚需求:在我们的web框架中增加一个通用的appkey白名单验证功能,希望它的扩展性更好。这个web框架是本系的先行者基于spring-boot实现的。它介于业务和Spring框架之间,做一些偏向于业务的通用功能,比如日志输出、功能切换、通用参数分析等。它通常对业务是透明的。最近一直在忙着做需求,写好代码,根本没有注意到它的存在。对于传统AOP中的这种需求,首先想到的当然是Spring-boot提供的AOP接口。只需要在Controller方法前添加一个切点,然后对切点进行处理即可。其使用实现步骤如下:使用@Aspect声明切面类WhitelistAspect;在切点类中添加一个切点whitelistPointcut(),为了实现这个切点的灵活可组装能力,这里我们不使用execution来拦截所有,而是添加一个注解@Whitelist,注解的方法会验证白名单。在切面类中使用spring的AOP注解@Before声明一个通知方法checkWhitelist(),用于在Controller方法执行前验证白名单。切面类的伪代码如下//可以使用joinPoint.getArgs()获取Controller方法的参数//可以使用whitelist变量获取注解参数}@Pointcut("@annotation(com.zhenbianshu.Whitelist)")publicvoidwhitelistPointCut(){}}在Controller方法中添加@Whitelist注解实现功能。本例中使用注解来声明切点,我实现了注解参数来声明待验证的白名单。如果后面需要添加其他白名单,比如UID来校验,可以注解这个Add方法,比如uid()来实现自定义校验。另外,spring的AOP还支持execution(执行方法)、bean(匹配特定名称的Bean对象的执行方法)、@Around(在目标函数执行时执行)、@After等切入点声明方法(方法执行后)等通知方法。就这样,功能实现了,但是领导不满意=_=,原因是项目中AOP用的太多了,过度使用了。我建议我改变它。好吧,我不得不开始了。拦截器Spring的拦截器(Interceptor)也很适合实现这个功能。顾名思义,拦截器就是在Controller中的Action执行之前,通过一些参数来判断是否执行这个方法。要实现拦截器,可以实现Spring的HandlerInterceptor接口。实现步骤如下:定义拦截器类AppkeyInterceptor类,实现HandlerInterceptor接口。实现它的preHandle()方法;通过preHandle方法中的注解和参数判断是否拦截请求,拦截请求时接口返回false;在自定义WebMvcConfigurerAdapter类中注册此拦截器;AppkeyInterceptor类如下:@ComponentpublicclassWhitelistInterceptorimplementsHandlerInterceptor{@OverridepublicbooleanpreHandle(HttpServletRequestrequest,HttpServletResponseresponse,Objecthandler)throwsException{Whitelistwhitelist=((HandlerMethod)handler).getMethodAnnotation(Whitelist.class);//白名单.values();通过白名单变量获取注解参数返回true;}@OverridepublicvoidpostHandle(HttpServletRequestrequest,HttpServletResponseresponse,Objecthandler,ModelAndViewmodelAndView)throwsException{//该方法在Controller方法执行完之后执行}@OverridepublicvoidrestquestafterReplerv(Http,HttpServletResponseresponse,Objecthandler,Exceptionex)throwsException{//视图视图渲染完成后执行}}要启用拦截器,必须显式配置启用它。这里我们使用WebMvcConfigurerAdapter来配置它。需要注意的是,继承它的MvcConfiguration需要在ComponentScan路径下。@ConfigurationpublicclassMvcConfigurationextendsWebMvcConfigurerAdapter{@OverridepublicvoidaddInterceptors(InterceptorRegistryregistry){registry.addInterceptor(newWhitelistInterceptor()).addPathPatterns("/*").order(1);//这里可以配置拦截器启用的路径当有多个拦截器时,如果有一个拦截器返回false,后面的请求方法将不会被执行}}另外需要注意的是,拦截器执行成功后,响应码为200,但响应数据为空。在使用拦截器实现功能后,leader终于来了个大动作:我们已经有了一个Auth参数,appkey可以从Auth参数中获取,是否在白名单中可以作为Auth的一种方式,为什么不在Auth检查时?emmm……吐血。ArgumentResolver参数解析器是Spring提供的一个解析自定义参数的工具。我们常用的@RequestParam注解都有它的影子。使用它,我们可以在进入ControllerAction之前将参数组合成我们想要的。Spring会维护一个ResolverList。当请求到来时,Spring发现有自定义类型参数(非基本类型),会依次尝试这些Resolver,直到一个Resolver能够解析出需要的参数。要实现参数解析器,请实现HandlerMethodArgumentResolver接口。实现并定义自定义参数类型AuthParam,其中有appkey相关字段;定义AuthParamResolver并实现HandlerMethodArgumentResolver接口;实现supportsParameter()接口方法适配AuthParam和AuthParamResolver;实现resolveArgument()接口方法解析reqest对象生成AuthParam对象,并在此处查看AuthParam,确认appkey是否在白名单中;在ControllerAction方法的签名中添加AuthParam参数来开启这个Resolver;实现的AuthParamResolver类如下:}@OverridepublicObjectresolveArgument(MethodParameterparameter,ModelAndViewContainermavContainer,NativeWebRequestwebRequest,WebDataBinderFactorybinderFactory)throwsException{Whitelistwhitelist=parameter.class)//通过webRequest验证白名单,whitelistreturnnewAuthParam();}}Extension当然参数解析器的使用也需要单独配置,我们也在WebMvcConfigurerAdapter中的配置:@ConfigurationpublicclassMvcConfigurationextendsWebMvcConfigurerAdapter{@OverridepublicvoidaddArgumentResolvers(List
