提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
本公司项目要求越来越严格,并且其中一条为加密,要求请求参数和返回值加密。今天简单的记录
加密传输使用前端加密,后端解密的方式。后端加密前端解密的方式。并且加密后不能被普通工具解密,需要使用密钥进行解密,保证数据的安全。那么现在encrypt-spring-boot-starter可以直接使用。
查看该依赖的源码,发现,只有使用依赖自定的res工具类包装返回值,并且他支持加密方式为AES,并不满足我们项目使用SM2的要求,同时,无法自定义加密方式。所以选择根据他的代码进行改造
首先,需要知道,我们通过在接口上进行注解的方式,来定义改接口是否使用加密和解密。那么首先定义加密和解密的注解
//加密
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD,ElementType.PARAMETER})
public @interface Decrypt {
}
//解密
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Encrypt {
}
使用aop进行切面解密和加密
package com.hisw.talent.encrypt;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.core.MethodParameter;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpInputMessage;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.RequestBodyAdviceAdapter;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Type;
/**
* @description:接口解密
* @date 2022/11/25 14:03
*/
@EnableConfigurationProperties(EncryptProperties.class)
@ControllerAdvice
public class DecryptRequest extends RequestBodyAdviceAdapter {
@Autowired
EncryptProperties encryptProperties;
public DecryptRequest() {
}
public boolean supports(MethodParameter methodParameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {
return methodParameter.hasMethodAnnotation(Decrypt.class) || methodParameter.hasParameterAnnotation(Decrypt.class);
}
public HttpInputMessage beforeBodyRead(final HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) throws IOException {
byte[] body = new byte[inputMessage.getBody().available()];
inputMessage.getBody().read(body);
try {
//this.encryptProperties.getKey() 这个key 根据你使用的加密方式来自定义AES需要满足16位,当然
//AESUtils.decrypt 可以替换为你需要的加密工具类,比如使用SM2,或者RSA非对称加密
byte[] decrypt = AESUtils.decrypt(body, this.encryptProperties.getKey().getBytes());
final ByteArrayInputStream bais = new ByteArrayInputStream(decrypt);
return new HttpInputMessage() {
public InputStream getBody() throws IOException {
return bais;
}
public HttpHeaders getHeaders() {
return inputMessage.getHeaders();
}
};
} catch (Exception var8) {
var8.printStackTrace();
return super.beforeBodyRead(inputMessage, parameter, targetType, converterType);
}
}
}
package com.hisw.talent.encrypt;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.hisw.talent.common.utils.Result;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
/**
* @description:接口加密
* @date 2022/11/25 14:02
*/
@EnableConfigurationProperties(EncryptProperties.class)
@ControllerAdvice
public class EncryptResponse implements ResponseBodyAdvice<Result> {
private ObjectMapper om = new ObjectMapper();
@Autowired
EncryptProperties encryptProperties;
public EncryptResponse() {
}
public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
return returnType.hasMethodAnnotation(Encrypt.class);
}
public Result beforeBodyWrite(Result body, MethodParameter returnType, MediaType selectedContentType, Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
byte[] keyBytes = this.encryptProperties.getKey().getBytes();
try {
/* if (body.getMsg() != null) {
body.setMsg(org.javaboy.encrypt.starter.utils.AESUtils.encrypt(body.getMsg().getBytes(), keyBytes));
}*/
if (body.getData() != null) {
//这个步骤可以替换为你的工具类 ,同时 Result 返回工具类替换为你项目中的
body.setData(AESUtils.encrypt(this.om.writeValueAsBytes(body.getData()), keyBytes));
}
} catch (Exception var9) {
var9.printStackTrace();
}
return body;
}
}
最后,在你controller 里 打上 注解来控制接口的加密和解密
方式为切面编程,难度不大,但是目前有个问题,AES加密 一旦 body数据太大,那么解密时会出现失败的情况,提示长度需要为16的倍数。RSA加密boby太长,需要使用分段解密。同时会造成前端解密后出现乱码的情况。SM2加密,同样会遇到解密失败的情况。需要04开头,但是过程中依然会报错。希望知道的同学解惑