1 概述
在项目中,为了保证数据的安全,我们常常会对传递的数据进行加密。常用的加密算法包括对称加密(AES)和非对称加密(RSA),这里针对SpringBoot搭建的项目,博主根据SpringBoot自动配置的原理写了一个RSA自动加密工具,实现自动加密返回数据、解密传入数据并映射成json。
2 项目结构
上图的项目结构其实和SpringBoot--自动配置Demo实现的项目结构基本一样。
- advice包里面存放的就是加密解密的主要工具。
- annotation就是需要用到的自动加密解密的注释。
- auto里面就是获取配置文件信息的类。
3 源码介绍
这里要要对传入参数进行解密和对传入参数进行加密,起主要作用的是EncryptRequestBodyAdvice和
EncryptResponseBodyAdvice这两个类,至于自动配置的讲解,我们这里就不做过多解释了,需要了解的可以参考我得前两篇文章(SpringBoot--自动配置原理解析和SpringBoot--自动配置Demo实现)。
(1)EncryptRequestBodyAdvice
/**
* 请求请求处理类(目前仅仅对requestbody有效)
* 对加了@Decrypt的方法的数据进行解密密操作
*
* @author: LIUTAO
* @Description:
* @Date: Created in 14:23 2018/6/7
* @Modified By:
*/
@ControllerAdvice
public class EncryptRequestBodyAdvice implements RequestBodyAdvice {
private Logger logger = LoggerFactory.getLogger(EncryptRequestBodyAdvice.class);
@Autowired
private SecurityProperties securityProperties;
@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 (parameter.getMethod().isAnnotationPresent(Decrypt.class) && !securityProperties.isDebug()) {
try {
return new DecryptHttpInputMessage(inputMessage, securityProperties.getPrivateKey(), securityProperties.getCharset());
} catch (Exception e) {
logger.error("数据解密失败", e);
}
}
return inputMessage;
}
@Override
public Object afterBodyRead(Object body, HttpInputMessage inputMessage, MethodParameter parameter, Type targetType,
Class<? extends HttpMessageConverter<?>> converterType) {
return body;
}
}
class DecryptHttpInputMessage implements HttpInputMessage {
private HttpHeaders headers;
private InputStream body;
public DecryptHttpInputMessage(HttpInputMessage inputMessage, String privateKey, String charset) throws Exception {
if(StringUtils.isEmpty(privateKey)){
throw new IllegalArgumentException("privateKey is null");
}
//获取请求内容
this.headers = inputMessage.getHeaders();
String content = IOUtils.toString(inputMessage.getBody(), charset);
//未加密数据不进行解密操作
String decryptBody;
if (content.startsWith("{")) {
decryptBody = content;
} else {
StringBuilder json = new StringBuilder();
content = content.replaceAll(" ", "+");
if (!StringUtils.isEmpty(content)) {
String[] contents = content.split("\\|");
for (int k = 0; k < contents.length; k++) {
String value = contents[k];
value = new String(RSAUtils.decryptByPrivateKey(Base64Utils.decode(value), privateKey), charset);
json.append(value);
}
}
decryptBody = json.toString();
}
this.body = IOUtils.toInputStream(decryptBody, charset);
}
@Override
public InputStream getBody() throws IOException {
return body;
}
@Override
public HttpHeaders getHeaders() {
return headers;
}
}
(2)EncryptResponseBodyAdvice
/**
* 请求响应处理类
* 对加了@Encrypt的方法的数据进行加密操作
*
* @author: LIUTAO
* @Description:
* @Date: Created in 14:23 2018/6/7
* @Modified By:
*/
@ControllerAdvice
public class EncryptResponseBodyAdvice implements ResponseBodyAdvice<Object> {
private Logger logger = LoggerFactory.getLogger(EncryptResponseBodyAdvice.class);
private ObjectMapper objectMapper = new ObjectMapper();
@Autowired
private SecurityProperties securityProperties;
private static ThreadLocal<Boolean> encryptLocal = new ThreadLocal<>();
public static void setEncryptStatus(boolean status) {
encryptLocal.set(status);
}
public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
return true;
}
public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType,
Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
// 可以通过调用EncryptResponseBodyAdvice.setEncryptStatus(false);来动态设置不加密操作
Boolean status = encryptLocal.get();
if (status != null && status == false) {
encryptLocal.remove();
return body;
}
boolean encrypt = false;
if (returnType.getMethod().isAnnotationPresent(Encrypt.class) && !securityProperties.isDebug()) {
encrypt = true;
}
if (encrypt) {
String privateKey = securityProperties.getPrivateKey();
try {
//获取到返回数据
String content = objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(body);
if (!StringUtils.hasText(privateKey)) {
throw new NullPointerException("请配置spring.encrypt.privatekeyc参数");
}
//进行加密操作
byte[] data = content.getBytes();
byte[] encodedData = RSAUtils.encryptByPrivateKey(data, privateKey);
String result = Base64Utils.encode(encodedData);
return result;
} catch (Exception e) {
logger.error("加密数据异常", e);
}
}
return body;
}
}
3 使用
这里的使用就比较简单了。
(1)在配置文件application.properties中添加配置内容。
spring.encrypt.privateKey=MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAMUrYQ/57Vx0DCF0I
d+yOVGLeODYJhnEGBxxnDQHYGr9qWQ+wlkLbn0bbQDhuRffdXX3MF73Equ6jXe0OLfT1nx00qx9KONy7WanUEYedy30
Zj6SqNppWF7s5LTgnnCMkSDLCzL35Wifxe/KdMtrBIeCExKD2lShA/F04V3rOGn1AgMBAAECgYALr2J1O+6hSA9f/C3
1v+49svJbAPRhGooDRYhoXPeN37KmSkHiXRcTOwjewIHjtE6VyyyGtEXa/5davMePvXI8m132hMfZa9RhRfxFhpLqYg
a5LREOYdon0LvXvsiTgexOglA3YkTQwdhzkGuhbQFQWdbvCC7rwFnbVaTB7z5LEQJBAOOu0BWFmKzOmk9A7hIqOb8sh
E+BAm0MMSc4GphO3EzpnEOWI90xsP3efttihNaqG8ZHma8GlyFhIPYNqnR/90cCQQDdsQz/mvlUG0xR5O3f+qIm6mw8
rlbe/1kVUVmEIyK3XJ0jnppoul9mPY91lXMX/tis0Gx/inN2TWn4XI+bnarjAkBBr14yx08Lk7Mq6CWGsg3k3Ffzg9m
KUjkgAmyRwjaGLeM6EGeaWcqhAv6AFkUSlRLcOi3ZM8KIC7hxo/GoGH7jAkEAvwRwM8m/raW71BCSmlwl3aw9yOdbON
gCVSj8Hav8nMuzJl7hov17d+fxNZqpSfKvlfAcnKSaKkQ32+U9ZBOtiwJAUwXqhQDp2FVdFeeJbQbw5pbLrpdtGYem5
34I2PRriBl7x48F0zo8upiK796TweKXPZKtBViwltp8zcKJdqqQ9A==
spring.encrypt.debug=false
上面的主要功能就是添加RSA加密和解密的私钥,并且将debug开关置为false(如果置为true将不会进行加密和解密操作)。
(2)在启动类上添加EnableSecurity注解
@SpringBootApplication
@ComponentScan(basePackages={"com.liutao.swagger"})
@EnableSecurity
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
(3)在需要加密的方法上添加Encrypt注解(解密为Decrypt)
@Encrypt
@ApiOperation(value="获取用户")
@GetMapping("user")
public User getUser(){
User user = new User();
user.setName("liutao");
user.setId("1212");
user.setPassword("123456");
return user;
}
运行代码,我们就可以看见自动解密和加密工具生效。
源码参考地址:RSA自动加密解密工具,喜欢就点个星星哦