SpringBoot--RSA自动加密解密工具

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/ONROAD0612/article/details/82050020

1 概述

在项目中,为了保证数据的安全,我们常常会对传递的数据进行加密。常用的加密算法包括对称加密(AES)和非对称加密(RSA),这里针对SpringBoot搭建的项目,博主根据SpringBoot自动配置的原理写了一个RSA自动加密工具,实现自动加密返回数据、解密传入数据并映射成json。

2 项目结构

上图的项目结构其实和SpringBoot--自动配置Demo实现的项目结构基本一样。

  1. advice包里面存放的就是加密解密的主要工具。
  2. annotation就是需要用到的自动加密解密的注释。
  3. 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自动加密解密工具,喜欢就点个星星哦

猜你喜欢

转载自blog.csdn.net/ONROAD0612/article/details/82050020