# open-api **Repository Path**: CaiGaoQing/open-api ## Basic Information - **Project Name**: open-api - **Description**: SpringMvc扩展自定义注解实现web解密 加密 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 1 - **Created**: 2018-08-17 - **Last Updated**: 2022-06-07 ## Categories & Tags **Categories**: Uncategorized **Tags**: 自定义注解, 加密解密 ## README 在项目中spring web服务为外部APP提供rest接口,为了 安全考虑 一般会加入自己的验证,常规 的签名+数据加密, 当然一个好的架构师或者负责人会在项目初期会考虑到的问题现老代码一般是在@Controller层每个request的开始和结尾处做的加密解密操作,这样的代码重复度很高,而且不利于更好的利用Spring框架的HttpMessageConverter达到java丰富类型的自动转换。因为APP\传到后台是加密后的数据。无法做到自动拆箱,所以说我们要在接受参数之前进行解密操作,在返回数据的时候进行加密操作! 2.实现方式 拦截器实现 切面拦截实现 自定义注解实现 1考虑前两种的性能,以及复用性本文采用 自定义注解去实现,当然这只是其中一部分原因。 2是看起来比较厉害对吧。(程序员都是骚的不行滴。O(∩_∩)O) 3.具体代码 包命名 我认为一份好的代码以及要有规矩,当然我是工作不太久,但是我在尽量对我的代码和各种命令写好。  com.xxx.open com.xxx.open.common.annotation --------------------  自定义注解包 com.xxx.common.container            --------------------   容器产量 com.xxx.common.entity                   --------------------   实体类 com.xxx.common.exception            --------------------   自定义异常 com.xxx.common.support               --------------------   对自定义注解算是的实现吧 com.xxx.util                                        -------------------- 加密解密工具类    2.自定义注解实现解密 #自定义自己的路径 import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** - @Author 蔡高情 - @Description //接收数据 自定义注解解密 - @Date 11:43 2018/7/9 0009 **/ @Target(ElementType.PARAMETER) @Retention(RetentionPolicy.RUNTIME) public @interface RequestBodyDecrypt { String value() default ""; boolean required() default true; }       在数据之前进行解密自动拆箱,但是spring自带的参数解析器不具有这种功能,只能尝试着用自定义参数解析器去解决。 自定义解析器需要实现HandlerMethodArgumentResolver接口,HandlerMethodArgumentResolver接口包含两个接口 // // Source code recreated from a .class file by IntelliJ IDEA // (powered by Fernflower decompiler) // package org.springframework.web.method.support; import org.springframework.core.MethodParameter; import org.springframework.web.bind.support.WebDataBinderFactory; import org.springframework.web.context.request.NativeWebRequest; import org.springframework.web.method.support.ModelAndViewContainer; public interface HandlerMethodArgumentResolver { //supportsParameter:用于判定是否需要处理该参数分解,返回true为需要,并会去调用下面的方法 boolean supportsParameter(MethodParameter var1); //真正用于处理参数分解的方法,返回的Object就是controller方法上的形参对象。 Object resolveArgument(MethodParameter var1, ModelAndViewContainer var2, NativeWebRequest var3, WebDataBinderFactory var4) throws Exception; } - package com.caigaoqing.tech.common.support; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import com.caigaoqing.tech.common.entity.JSONObjectWrapper; import com.caigaoqing.tech.common.annotation.RequestBodyDecrypt; import com.caigaoqing.tech.common.exception.SharechargerOpenException; import com.caigaoqing.tech.util.encrypt.AesCBC; import com.caigaoqing.tech.util.encrypt.GetRequestDataUtil; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.springframework.core.MethodParameter; import org.springframework.web.bind.support.WebDataBinderFactory; import org.springframework.web.context.request.NativeWebRequest; import org.springframework.web.method.support.HandlerMethodArgumentResolver; import org.springframework.web.method.support.ModelAndViewContainer; import javax.servlet.http.HttpServletRequest; import java.util.List; import java.util.Map; /** - @Author 蔡高情 - @Description 自定义HandlerMethodArgumentResolver解析 - @Date 12:36 2018/7/9 0009 - @Param - @return **/ @Slf4j public class RequestBodyDecryptArgumentResolver implements HandlerMethodArgumentResolver { //判断是否支持要转换的参数类型 @Override public boolean supportsParameter(MethodParameter parameter) { return parameter.hasParameterAnnotation(RequestBodyDecrypt.class); } //当支持后进行相应的转换 @Override public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception{ HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class); //获取全部的所有参数 Map parMap=request.getParameterMap(); //获取加密的postBody String encryBody= GetRequestDataUtil.getEncryBody(request); //进行解密操作返回解密后的书数据getRequestBody(); String body=getRequestBody(encryBody); Object result; log.debug("THE CLIENT POST BODY " +body); //下面写的是拆箱操作,其实应该还有很多方式,这里举个最为明白的吧 Class clazz = parameter.getParameterType(); if (StringUtils.isEmpty(body)) { return null; } if (clazz.equals(List.class)){ result= JSON.parseArray(body); } // 利用fastjson转换为对应的类型 if(JSONObjectWrapper.class.isAssignableFrom(parameter.getParameterType())){ result= new JSONObjectWrapper(JSON.parseObject(body)); } else { result= JSON.parseObject(body.toString(), parameter.getParameterType()); } //如果有key则获取key的值返回 String key=parameter.getParameterAnnotation(RequestBodyDecrypt.class).value(); if (StringUtils.isNotEmpty(key)){ JSONObject data= JSON.parseObject(body.toString()); //获取json的key result=data.get(key); } return result; } /** * 获取提交的post json * 验证加密 * @param * @return */ private String getRequestBody(String encryBody) throws SharechargerOpenException ,Exception{ //这里可以做appId,appk的校验,这里涉及到企业代码不写了,用base64代替 //解析完后 的传递参数 //这里可以动态获取,我这写死了 String data=AesCBC.decrypt(encryBody,"1234567812345678","1234567812345678"); return data; } } 把自定义的解析器加入到springmvc中 package com.sharecharger.open.common.support; import org.springframework.format.FormatterRegistry; import org.springframework.http.converter.HttpMessageConverter; import org.springframework.validation.MessageCodesResolver; import org.springframework.validation.Validator; import org.springframework.web.method.support.HandlerMethodArgumentResolver; import org.springframework.web.method.support.HandlerMethodReturnValueHandler; import org.springframework.web.servlet.HandlerExceptionResolver; import org.springframework.web.servlet.config.annotation.*; import java.util.List; public class RequestBodyWrapFactoryBean implements WebMvcConfigurer { @Override public void configurePathMatch(PathMatchConfigurer pathMatchConfigurer) { } @Override public void configureContentNegotiation(ContentNegotiationConfigurer contentNegotiationConfigurer) { } @Override public void configureAsyncSupport(AsyncSupportConfigurer asyncSupportConfigurer) { } @Override public void configureDefaultServletHandling(DefaultServletHandlerConfigurer defaultServletHandlerConfigurer) { } @Override public void addFormatters(FormatterRegistry formatterRegistry) { } @Override public void addInterceptors(InterceptorRegistry interceptorRegistry) { } @Override public void addResourceHandlers(ResourceHandlerRegistry resourceHandlerRegistry) { } @Override public void addCorsMappings(CorsRegistry corsRegistry) { } @Override public void addViewControllers(ViewControllerRegistry viewControllerRegistry) { } @Override public void configureViewResolvers(ViewResolverRegistry viewResolverRegistry) { } /** * 添加自定义解析器 * @param */ @Override public void addArgumentResolvers(List list) { RequestBodyDecryptArgumentResolver decorator = new RequestBodyDecryptArgumentResolver(); if (decorator instanceof HandlerMethodArgumentResolver) { list.add(decorator); } } @Override public void addReturnValueHandlers(List list) { } @Override public void configureMessageConverters(List> list) { } @Override public void extendMessageConverters(List> list) { } @Override public void configureHandlerExceptionResolvers(List list) { } @Override public void extendHandlerExceptionResolvers(List list) { } @Override public Validator getValidator() { return null; } @Override public MessageCodesResolver getMessageCodesResolver() { return null; } } 4.注意事项 我代码写的不好,还请大家多多见谅!!!! 这个注解对map的非常不友好。map会优先被springmvc的自带解析器有限处理。在下节在说解决这个问题!