SpringBoot接口加密解密统一处理

网友投稿 412 2022-12-30


SpringBoot接口加密解密统一处理

我们与客户端的接口交互中,为了更高的安全性,我们可能需要对接口加密(请求参数加密,服务端解密)、返回信息加密(服务端加密,客户端解密),但是也不是所有的接口都这样,有些接口可能不需要,我们可以使用注解来轻松达到此要求。

将接口参数的加密解密和返回信息的加密解密分开,分别定义注解,利用Controller的ControllerAdvice来拦截所有的请求,在其中判断是否需要加密解密,即可达到要求。

使用方法:使用 DecryptRequest 和 EncryptResponse 注解即可,可以放在Controller的类和方法上,其中一个为false就不执行了。像这样:

@RestController

@RequestMapping("/test")

//@DecryptRequest

@EncryptResponse

public class TestController {

@Autowired

@Qualifier("rrCrypto")

private Crypto crypto;

@DecryptRequest(false)

@EncryptResponse(false)

@RequestMapping(value = "/enc" , method = RequestMethod.POST)

public String enc(@RequestBody String body){

return crypto.encrypt(body);

}

}

定义参数解密的注解,DecryptRequest。

/**

* 解密注解

*

*

加了此注解的接口(true)将进行数据解密操作(post的body) 可

* 以放在类上,可以放在方法上

* @author xiongshiyan

*/

@Target({ElementType.METHOD , ElementType.TYPE})

@Retention(RetentionPolicy.RUNTIME)

@Documented

public @interface DecryptRequest {

/**

* 是否对body进行解密

*/

boolean value() default true;

}

定义返回信息加密的注解,EncryptResponse。

/**

* 加密注解

*

*

加了此注解的接口(true)将进行数据加密操作

* 可以放在类上,可以放在方法上

* @author 熊诗言

*/

@Target({ElementType.METHOD , ElementType.TYPE})

@Retention(RetentionPolicy.RUNTIME)

@Documented

public @interface EncryptResponse {

/**

* 是否对结果加密

*/

boolean value() default true;

}

这两个注解可以放在类和方法上,遵循一样的逻辑,即:类上的注解 && 方法上的注解,一方没有即为true,都为false为false。逻辑主要在 NeedCrypto 中。

/**

* 判断是否需要加解密

* @author xiongshiyan at 2018/8/30 , contact me with email yanshixiong@126.com or phone 15208384257

*/

class NeedCrypto {

private NeedCrypto(){}

/**

* 是否需要对结果加密

* 1.类上标注或者方法上标注,并且都为true

* 2.有一个标注为false就不需要加密

*/

static boolean needEncrypt(MethodParameter returnType) {

boolean encrypt = false;

boolean classPresentAnno = returnType.getContainingClass().isAnnotationPresent(EncryptResponse.class);

boolean methodPresentAnno = returnType.getMethod().isAnnotationPresent(EncryptResponse.class);

if(classPresentAnno){

//类上标注的是否需要加密

encrypt = returnType.getContainingClass().getAnnotahttp://tion(EncryptResponse.class).value();

//类不加密,所有都不加密

if(!encrypt){

return false;

}

}

if(methodPresentAnno){

//方法上标注的是否需要加密

encrypt = returnType.getMethod().getAnnotation(EncryptResponse.class).value();

}

return encrypt;

}

/**

* 是否需要参数解密

* 1.类上标注或者方法上标注,并且都为true

* 2.有一个标注为false就不需要解密

*/

static boolean needDecrypt(MethodParameter parameter) {

boolean encrypt = false;

boolean classPresentAnno = parameter.getContainingClass().isAnnotationPresent(DecryptRequest.class);

boolean methodPresentAnno = parameter.getMethod().isAnnotationPresent(DecryptRequest.class);

if(classPresentAnno){

//类上标注的是否需要解密

encrypt = parameter.getContainingClass().getAnnotation(DecryptRequest.class).value();

//类不加密,所有都不加密

if(!encrypt){

return false;

}

}

if(methodPresentAnno){

//方法上标注的是否需要解密

encrypt = parameter.getMethod().getAnnotation(DecryptRequest.class).value();

}

return encrypt;

}

}

然后定义ControllerAdvice,对于请求解密的,定义 DecryptRequestBodyAdvice ,实现 RequestBodyAdvice 。

/**

* 请求数据接收处理类

*

* 对加了@Decrypt的方法的数据进行解密操作

*

* 只对 @RequestBody 参数有效

* @author xiongshiyan

*/

@ControllerAdvice

@ConditionalOnProperty(prefix = "spring.crypto.request.decrypt", name = "enabled" , havingValue = "true", matchIfMissing = true)

public class DecryptRequestBodyAdvice implements RequestBodyAdvice {

@Value("${spring.crypto.request.decrypt.charset:UTF-8}")

private String charset = "UTF-8";

@Autowired

@Qualifier("rrCrypto")

private Crypto crypto;

@Override

public boolean supports(MethodParameter methodParameter, Type targetType,

Class extends HttpMessageConverter>> converterType) {

return true;

}

@Override

public Object handleEmptyBody(Object body, HttpInputMessage inputMessage, MethodParameter parameter,

Type targetType, Class extends HttpMessageConverter>> converterType) {

return body;

}

@Override

public HttpInputMessage beforeBodyRead(HttpInputMessage inputMessage, MethodParameter parameter, Type targetType,

Class extends HttpMessageConverter>> converterType) throws IOException {

if( NeedCrypto.needDecrypt(parameter) ){

return new DecryptHttpInputMessage(inputMessage , charset , crypto);

}

return inputMessage;

}

@Override

public Object afterBodyRead(Object body, HttpInputMessage inpuhttp://tMessage, MethodParameter parameter, Type targetType,

Class extends HttpMessageConverter>> converterType) {

return body;

}

}

标上注解 ConditionalOnProperty 表示只有条件为true的时候才开启解密功能,一个配置即可打开或者关闭解密功能。真正的解密逻辑留给 DecryptHttpInputMessage , 它又委托给 Crypto。

/**

*

* @author xiongshiyan

*/

public class DecryptHttpInputMessage implements HttpInputMessage {

private HttpInputMessage inputMessage;

private String charset;

private Crypto crypto;

public DecryptHttpInputMessage(HttpInputMessage inputMessage, String charset , Crypto crypto) {

this.inputMessage = inputMessage;

this.charset = charset;

this.crypto = crypto;

}

@Override

public InputStream getBody() throws IOException {

String content = IoUtil.read(inputMessage.getBody() , charset);

String decryptBody = crypto.decrypt(content, charset);

return new ByteArrayInputStream(decryptBody.getBytes(charset));

}

@Override

public HttpHeaders getHeaders() {

return inputMessage.getHeaders();

}

}

对于返回值加密,定义 EncryptResponseBodyAdvice,实现 ResponseBodyAdvice。

/**

* 请求响应处理类

*

* 对加了@Encrypt的方法的数据进行加密操作

*

* @author 熊诗言

*

*/

@ControllerAdvice

@ConditionalOnProperty(prefix = "spring.crypto.response.encrypt", name = "enabled" , havingValue = "true", matchIfMissing = true)

public class EncryptResponseBodyAdvice implements ResponseBodyAdvice {

@Value("${spring.crypto.request.decrypt.charset:UTF-8}")

private String charset = "UTF-8";

@Autowired

@Qualifier("rrCrypto")

private Crypto crypto;

@Override

public boolean supports(MethodParameter returnType, Class extends HttpMessageConverter>> converterType) {

return true;

}

@Override

public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType,

Class extends HttpMessageConverter>> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {

boolean encrypt = NeedCrypto.needEncrypt(returnType);

if( !encrypt ){

return body;

}

if(!(body instanceof ResponseMsg)){

return body;

}

//只针对ResponseMsg的data进行加密

ResponseMsg responseMsg = (ResponseMsg) body;

Object data = responseMsg.getData();

if(null == data){

return body;

}

String xx;

Class> dataClass = data.getClass();

if(dataClass.isPrimitive() || (data instanceof String)){

xx = String.valueOf(data);

}else {

//javaBean、Map、List等先序列化

if(List.class.isAssignableFrom(dataClass)){

xx = jsonUtil.serializeList((List) data);

}else if(Map.class.isAssignableFrom(dataClass)){

xx = JsonUtil.serializeMap((Map) data);

}else {

xx = JsonUtil.serializeJavaBean(data);

}

}

responseMsg.setData(crypto.encrypt(xx, charset));

return responseMsg;

}

}

真正的加密逻辑委托给 Crypto ,这是一个加密解密的接口,有很多实现类,参见:链接

/**

* Request-Response加解密体系的加解密方式

* @author xiongshiyan at 2018/8/14 , contact me with email yanshixiong@126.com or phone 15208384257

*/

@Configuration

public class RRCryptoConfig {

/**

* 加密解密方式使用一样的

*/

@Bean("rrCrypto")

public Crypto rrCrypto(){

return new AesCrypto("密钥key");

}

}

至此,一个完美的对接口的加密解密就实现了。


版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:开源api网关选型(api网关设计原则)
下一篇:网络直报系统接口设计图(网络接口原理图)
相关文章

 发表评论

暂时没有评论,来抢沙发吧~