SpringBoot实现接口数据的加解密功能
一、加密方案介绍
对接口的加密解密操作主要有下面两种方式:
自定义消息转换器
优势:仅需实现接口,配置简单。
劣势:仅能对同一类型的MediaType进行加解密操作,不灵活。
使用spring提供的接口RequestBodyAdvice和ResponseBodyAdvice
优势:可以按照请求的Referrer、Header或url进行判断,按照特定需要进行加密解密。
比如在一个项目升级的时候,新开发功能的接口需要加解密,老功能模块走之前的逻辑不加密,这时候就只能选择上面的第二种方式了,下面主要介绍下第二种方式加密、解密的过程。
二、实现原理
RequestBodyAdvice可以理解为在@RequestBody之前需要进行的操作,ResponseBodyAdvice可以理解为在@ResponseBody之后进行的操作,所以当接口需要加解密时,在使用@RequestBody接收前台参数之前可以先在RequestBodyAdvice的实现类中进行参数的解密,当操作结束需要返回数据时,可以在@ResponseBody之后进入ResponseBodyAdvice的实现类中进行参数的加密。
RequestBodyAdvice处理请求的过程:
RequestBodyAdvice源码如下:
publicinterfaceRequestBodyAdvice{
booleansupports(MethodParametermethodParameter,TypetargetType,
Class>converterType);
HttpInputMessagebeforeBodyRead(HttpInputMessageinputMessage,MethodParameterparameter,
TypetargetType,Class>converterType)throwsIOException;
ObjectafterBodyRead(Objectbody,HttpInputMessageinputMessage,MethodParameterparameter,
TypetargetType,Class>converterType);
@Nullable
ObjecthandleEmptyBody(@NullableObjectbody,HttpInputMessageinputMessage,MethodParameterparameter,
TypetargetType,Class>converterType);
}
调用RequestBodyAdvice实现类的部分代码如下:
protectedObjectreadWithMessageConverters(HttpInputMessageinputMessage,MethodParameterparameter, TypetargetType)throwsIOException,HttpMediaTypeNotSupportedException,HttpMessageNotReadableException{ MediaTypecontentType; booleannoContentType=false; try{ contentType=inputMessage.getHeaders().getContentType(); } catch(InvalidMediaTypeExceptionex){ thrownewHttpMediaTypeNotSupportedException(ex.getMessage()); } if(contentType==null){ noContentType=true; contentType=MediaType.APPLICATION_OCTET_STREAM; } Class>contextClass=parameter.getContainingClass(); Class targetClass=(targetTypeinstanceofClass?(Class )targetType:null); if(targetClass==null){ ResolvableTyperesolvableType=ResolvableType.forMethodParameter(parameter); targetClass=(Class )resolvableType.resolve(); } HttpMethodhttpMethod=(inputMessageinstanceofHttpRequest?((HttpRequest)inputMessage).getMethod():null); Objectbody=NO_VALUE; EmptyBodyCheckingHttpInputMessagemessage; try{ message=newEmptyBodyCheckingHttpInputMessage(inputMessage); for(HttpMessageConverter>converter:this.messageConverters){ Class >converterType=(Class >)converter.getClass(); GenericHttpMessageConverter>genericConverter= (converterinstanceofGenericHttpMessageConverter?(GenericHttpMessageConverter>)converter:null); if(genericConverter!=null?genericConverter.canRead(targetType,contextClass,contentType): (targetClass!=null&&converter.canRead(targetClass,contentType))){ if(logger.isDebugEnabled()){ logger.debug("Read["+targetType+"]as\""+contentType+"\"with["+converter+"]"); } if(message.hasBody()){ HttpInputMessagemsgToUse= getAdvice().beforeBodyRead(message,parameter,targetType,converterType); body=(genericConverter!=null?genericConverter.read(targetType,contextClass,msgToUse): ((HttpMessageConverter )converter).read(targetClass,msgToUse)); body=getAdvice().afterBodyRead(body,msgToUse,parameter,targetType,converterType); } else{ body=getAdvice().handleEmptyBody(null,message,parameter,targetType,converterType); } break; } } } catch(IOExceptionex){ thrownewHttpMessageNotReadableException("I/Oerrorwhilereadinginputmessage",ex); } if(body==NO_VALUE){ if(httpMethod==null||!SUPPORTED_METHODS.contains(httpMethod)|| (noContentType&&!message.hasBody())){ returnnull; } thrownewHttpMediaTypeNotSupportedException(contentType,this.allSupportedMediaTypes); } returnbody; }
从上面源码可以到当converter.canRead()和message.hasBody()都为true的时候,会调用beforeBodyRead()和afterBodyRead()方法,所以我们在实现类的afterBodyRead()中添加解密代码即可。
ResponseBodyAdvice处理响应的过程:
ResponseBodyAdvice源码如下:
publicinterfaceResponseBodyAdvice{ booleansupports(MethodParameterreturnType,Class>converterType); @Nullable TbeforeBodyWrite(@NullableTbody,MethodParameterreturnType,MediaTypeselectedContentType, Class>selectedConverterType, ServerHttpRequestrequest,ServerHttpResponseresponse); }
调用ResponseBodyAdvice实现类的部分代码如下:
if(selectedMediaType!=null){
selectedMediaType=selectedMediaType.removeQualityValue();
for(HttpMessageConverter>converter:this.messageConverters){
GenericHttpMessageConvertergenericConverter=
(converterinstanceofGenericHttpMessageConverter?(GenericHttpMessageConverter>)converter:null);
if(genericConverter!=null?
((GenericHttpMessageConverter)converter).canWrite(declaredType,valueType,selectedMediaType):
converter.canWrite(valueType,selectedMediaType)){
outputValue=(T)getAdvice().beforeBodyWrite(outputValue,returnType,selectedMediaType,
(Class>)converter.getClass(),
inputMessage,outputMessage);
if(outputValue!=null){
addContentDispositionHeader(inputMessage,outputMessage);
if(genericConverter!=null){
genericConverter.write(outputValue,declaredType,selectedMediaType,outputMessage);
}
else{
((HttpMessageConverter)converter).write(outputValue,selectedMediaType,outputMessage);
}
if(logger.isDebugEnabled()){
logger.debug("Written["+outputValue+"]as\""+selectedMediaType+
"\"using["+converter+"]");
}
}
return;
}
}
}
从上面源码可以到当converter.canWrite()为true的时候,会调用beforeBodyWrite()方法,所以我们在实现类的beforeBodyWrite()中添加解密代码即可。
三、实战
新建一个springboot项目spring-boot-encry,按照下面步骤操作。
pom.xml中引入jar
org.springframework.boot spring-boot-starter-web org.projectlombok lombok true org.springframework.boot spring-boot-starter-test test org.junit.vintage junit-vintage-engine com.alibaba fastjson 1.2.60
请求参数解密拦截类
DecryptRequestBodyAdvice代码如下:
/**
*请求参数解密操作
**@Author:Java碎碎念
*@Date:2019/10/2421:31
*
*/
@Component
@ControllerAdvice(basePackages="com.example.springbootencry.controller")
@Slf4j
publicclassDecryptRequestBodyAdviceimplementsRequestBodyAdvice{
@Override
publicbooleansupports(MethodParametermethodParameter,TypetargetType,Class>converterType){
returntrue;
}
@Override
publicHttpInputMessagebeforeBodyRead(HttpInputMessageinputMessage,MethodParametermethodParameter,TypetargetType,Class>selectedConverterType)throwsIOException{
returninputMessage;
}
@Override
publicObjectafterBodyRead(Objectbody,HttpInputMessageinputMessage,MethodParameterparameter,TypetargetType,Class>converterType){
StringdealData=null;
try{
//解密操作
MapdataMap=(Map)body;
StringsrcData=dataMap.get("data");
dealData=DesUtil.decrypt(srcData);
}catch(Exceptione){
log.error("异常!",e);
}
returndealData;
}
@Override
publicObjecthandleEmptyBody(@NullableObjectvar1,HttpInputMessagevar2,MethodParametervar3,Typevar4,Class>var5){
log.info("3333");
returnvar1;
}
}
响应参数加密拦截类
EncryResponseBodyAdvice代码如下:
/** *请求参数解密操作 * *@Author:Java碎碎念 *@Date:2019/10/2421:31 * */ @Component @ControllerAdvice(basePackages="com.example.springbootencry.controller") @Slf4j publicclassEncryResponseBodyAdviceimplementsResponseBodyAdvice
新建controller类
TestController代码如下:
/***@Author:Java碎碎念
*@Date:2019/10/2421:40
*/
@RestController
publicclassTestController{
Loggerlog=LoggerFactory.getLogger(getClass());
/**
*响应数据加密
*/
@RequestMapping(value="/sendResponseEncryData")
publicResultsendResponseEncryData(){
Resultresult=Result.createResult().setSuccess(true);
result.setDataValue("name","Java碎碎念");
result.setDataValue("encry",true);
returnresult;
}
/**
*获取解密后的请求参数
*/
@RequestMapping(value="/getRequestData")
publicResultgetRequestData(@RequestBodyObjectobject){
log.info("controller接收的参数object={}",object.toString());
Resultresult=Result.createResult().setSuccess(true);
returnresult;
}
}
其他类在源码中,后面有github地址
四、测试
访问响应数据加密接口
使用postman发请求http://localhost:8888/sendResponseEncryData,可以看到返回数据已加密,请求截图如下:
响应数据加密截图
后台也打印相关的日志,内容如下:
接口=/sendResponseEncryData
原始数据={"data":{"encry":true,"name":"Java碎碎念"},"success":true}
加密后数据=vJc26g3SQRU9gAJdG7rhnAx6Ky/IhgioAgdwi6aLMMtyynAB4nEbMxvDsKEPNIa5bQaT7ZAImAL7
3VeicCuSTA==
访问请求数据解密接口
使用postman发请求http://localhost:8888/getRequestData,可以看到请求数据已解密,请求截图如下:
请求数据解密截图
后台也打印相关的日志,内容如下:
接收到原始请求数据={"data":"VwLvdE8N6FuSxn/jRrJavATopaBA3M1QEN+9bkuf2jPwC1eSofgahQ=="}
解密后数据={"name":"Java碎碎念","des":"请求参数"}
五、踩到的坑
测试解密请求参数时候,请求体一定要有数据,否则不会调用实现类触发解密操作。
到此SpringBoot中如何灵活的实现接口数据的加解密功能的功能已经全部实现,有问题欢迎留言沟通哦!
完整源码地址:https://github.com/suisui2019/springboot-study
总结
以上所述是小编给大家介绍的SpringBoot实现接口数据的加解密功能,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对毛票票网站的支持!
如果你觉得本文对你有帮助,欢迎转载,烦请注明出处,谢谢!
声明:本文内容来源于网络,版权归原作者所有,内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎发送邮件至:czq8825#qq.com(发邮件时,请将#更换为@)进行举报,并提供相关证据,一经查实,本站将立刻删除涉嫌侵权内容。