SpringBoot接口加密解密统一处理
我们与客户端的接口交互中,为了更高的安全性,我们可能需要对接口加密(请求参数加密,服务端解密)、返回信息加密(服务端加密,客户端解密),但是也不是所有的接口都这样,有些接口可能不需要,我们可以使用注解来轻松达到此要求。
将接口参数的加密解密和返回信息的加密解密分开,分别定义注解,利用Controller的ControllerAdvice来拦截所有的请求,在其中判断是否需要加密解密,即可达到要求。
使用方法:使用DecryptRequest和EncryptResponse注解即可,可以放在Controller的类和方法上,其中一个为false就不执行了。像这样:
@RestController
@RequestMapping("/test")
//@DecryptRequest
@EncryptResponse
publicclassTestController{
@Autowired
@Qualifier("rrCrypto")
privateCryptocrypto;
@DecryptRequest(false)
@EncryptResponse(false)
@RequestMapping(value="/enc",method=RequestMethod.POST)
publicStringenc(@RequestBodyStringbody){
returncrypto.encrypt(body);
}
}
定义参数解密的注解,DecryptRequest。
/** *解密注解 * *加了此注解的接口(true)将进行数据解密操作(post的body)可 *以放在类上,可以放在方法上
*@authorxiongshiyan */ @Target({ElementType.METHOD,ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented public@interfaceDecryptRequest{ /** *是否对body进行解密 */ booleanvalue()defaulttrue; }
定义返回信息加密的注解,EncryptResponse。
/** *加密注解 * *加了此注解的接口(true)将进行数据加密操作 *可以放在类上,可以放在方法上
*@author熊诗言 */ @Target({ElementType.METHOD,ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented public@interfaceEncryptResponse{ /** *是否对结果加密 */ booleanvalue()defaulttrue; }
这两个注解可以放在类和方法上,遵循一样的逻辑,即:类上的注解&&方法上的注解,一方没有即为true,都为false为false。逻辑主要在 NeedCrypto中。
/**
*判断是否需要加解密
*@authorxiongshiyanat2018/8/30,contactmewithemailyanshixiong@126.comorphone15208384257
*/
classNeedCrypto{
privateNeedCrypto(){}
/**
*是否需要对结果加密
*1.类上标注或者方法上标注,并且都为true
*2.有一个标注为false就不需要加密
*/
staticbooleanneedEncrypt(MethodParameterreturnType){
booleanencrypt=false;
booleanclassPresentAnno=returnType.getContainingClass().isAnnotationPresent(EncryptResponse.class);
booleanmethodPresentAnno=returnType.getMethod().isAnnotationPresent(EncryptResponse.class);
if(classPresentAnno){
//类上标注的是否需要加密
encrypt=returnType.getContainingClass().getAnnotation(EncryptResponse.class).value();
//类不加密,所有都不加密
if(!encrypt){
returnfalse;
}
}
if(methodPresentAnno){
//方法上标注的是否需要加密
encrypt=returnType.getMethod().getAnnotation(EncryptResponse.class).value();
}
returnencrypt;
}
/**
*是否需要参数解密
*1.类上标注或者方法上标注,并且都为true
*2.有一个标注为false就不需要解密
*/
staticbooleanneedDecrypt(MethodParameterparameter){
booleanencrypt=false;
booleanclassPresentAnno=parameter.getContainingClass().isAnnotationPresent(DecryptRequest.class);
booleanmethodPresentAnno=parameter.getMethod().isAnnotationPresent(DecryptRequest.class);
if(classPresentAnno){
//类上标注的是否需要解密
encrypt=parameter.getContainingClass().getAnnotation(DecryptRequest.class).value();
//类不加密,所有都不加密
if(!encrypt){
returnfalse;
}
}
if(methodPresentAnno){
//方法上标注的是否需要解密
encrypt=parameter.getMethod().getAnnotation(DecryptRequest.class).value();
}
returnencrypt;
}
}
然后定义ControllerAdvice,对于请求解密的,定义DecryptRequestBodyAdvice,实现 RequestBodyAdvice。
/** *请求数据接收处理类
* *对加了@Decrypt的方法的数据进行解密操作
* *只对@RequestBody参数有效 *@authorxiongshiyan */ @ControllerAdvice @ConditionalOnProperty(prefix="spring.crypto.request.decrypt",name="enabled",havingValue="true",matchIfMissing=true) publicclassDecryptRequestBodyAdviceimplementsRequestBodyAdvice{ @Value("${spring.crypto.request.decrypt.charset:UTF-8}") privateStringcharset="UTF-8"; @Autowired @Qualifier("rrCrypto") privateCryptocrypto; @Override publicbooleansupports(MethodParametermethodParameter,TypetargetType, Class>converterType){ returntrue; } @Override publicObjecthandleEmptyBody(Objectbody,HttpInputMessageinputMessage,MethodParameterparameter, TypetargetType,Class>converterType){ returnbody; } @Override publicHttpInputMessagebeforeBodyRead(HttpInputMessageinputMessage,MethodParameterparameter,TypetargetType, Class>converterType)throwsIOException{ if(NeedCrypto.needDecrypt(parameter)){ returnnewDecryptHttpInputMessage(inputMessage,charset,crypto); } returninputMessage; } @Override publicObjectafterBodyRead(Objectbody,HttpInputMessageinputMessage,MethodParameterparameter,TypetargetType, Class>converterType){ returnbody; } }
标上注解ConditionalOnProperty表示只有条件为true的时候才开启解密功能,一个配置即可打开或者关闭解密功能。真正的解密逻辑留给DecryptHttpInputMessage,它又委托给Crypto。
/**
*
*@authorxiongshiyan
*/
publicclassDecryptHttpInputMessageimplementsHttpInputMessage{
privateHttpInputMessageinputMessage;
privateStringcharset;
privateCryptocrypto;
publicDecryptHttpInputMessage(HttpInputMessageinputMessage,Stringcharset,Cryptocrypto){
this.inputMessage=inputMessage;
this.charset=charset;
this.crypto=crypto;
}
@Override
publicInputStreamgetBody()throwsIOException{
Stringcontent=IoUtil.read(inputMessage.getBody(),charset);
StringdecryptBody=crypto.decrypt(content,charset);
returnnewByteArrayInputStream(decryptBody.getBytes(charset));
}
@Override
publicHttpHeadersgetHeaders(){
returninputMessage.getHeaders();
}
}
对于返回值加密,定义EncryptResponseBodyAdvice,实现 ResponseBodyAdvice。
/** *请求响应处理类
* *对加了@Encrypt的方法的数据进行加密操作 * *@author熊诗言 * */ @ControllerAdvice @ConditionalOnProperty(prefix="spring.crypto.response.encrypt",name="enabled",havingValue="true",matchIfMissing=true) publicclassEncryptResponseBodyAdviceimplementsResponseBodyAdvice
真正的加密逻辑委托给Crypto,这是一个加密解密的接口,有很多实现类,参见:链接
/**
*Request-Response加解密体系的加解密方式
*@authorxiongshiyanat2018/8/14,contactmewithemailyanshixiong@126.comorphone15208384257
*/
@Configuration
publicclassRRCryptoConfig{
/**
*加密解密方式使用一样的
*/
@Bean("rrCrypto")
publicCryptorrCrypto(){
returnnewAesCrypto("密钥key");
}
}
至此,一个完美的对接口的加密解密就实现了。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持毛票票。