RSA加密和AES加密使用了hutool工具包
依赖
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.7.13</version>
</dependency>
加解密工具类
public class EncryptionUtil {
// rsa秘钥
private final static String PRIVATE_KEY = "...";
// rsa公钥
private final static String PUBLIC_KEY = "...";
// 创建rsa加密对象 服务端公钥是不需要的,公钥提供给前端就行
private final static RSA RSA = new RSA(PRIVATE_KEY, PUBLIC_KEY);
/**
* 获取加密返回
*/
public static EncryptionResponse getEncodeResponse(String encryptionData) {
// 生成随机aes秘钥
byte[] key = getRandomKey();
AES aes = SecureUtil.aes(key);
// 加密aes key
String aesKey = RSA.encryptBase64(key, KeyType.PrivateKey);
// aes加密内容
String encryptString = aes.encryptBase64(encryptionData);
return new EncryptionResponse(aesKey, encryptString);
}
/**
* 获取解密返回流
*/
public static InputStream getDecodeInputStream(EncryptionRequest request) {
String encryptionKey = request.getEncryptionKey();
// rsa解密 获取aes秘钥
byte[] decryptKey = RSA.decrypt(encryptionKey, KeyType.PrivateKey);
// aes进行解密内容 获取输入流(方便整合springMVC)
AES aes = SecureUtil.aes(decryptKey);
byte[] decryptData = aes.decrypt(request.getEncryptionData());
return IoUtil.toStream(decryptData);
}
/**
* 随机生成aes的秘钥
*/
private static byte[] getRandomKey() {
return RandomUtil.randomString(16).getBytes();
}
}
进行加密解密的一些对象准备
#配置
request:
encode: true # 配置接口返回是否进行加密
decode: true # 配置接口入参是否进行解密
// 两个实体对象,用来表示请求和返回结果
@Data
@AllArgsConstructor
public class EncryptionResponse {
// rsa加密后的aes秘钥
private String encryptionKey;
// aes加密后的数据内容
private String encryptionData;
}
@Data
public class EncryptionRequest {
// 同response
private String encryptionKey;
private String encryptionData;
}
// 自定义注解,方法上有这个注解的进行加密和解密的操作
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface SecurityParameter {
/**
* 入参是否解密,默认解密
*/
boolean inDecode() default true;
/**
* 出参是否加密,默认加密
*/
boolean outEncode() default true;
}
SpringMvc的responseBody增强
// 进行加密的advice,@ConditionalOnProperty配置request.encode等于true的时候才进行加密
@Slf4j
@ControllerAdvice
@ConditionalOnProperty(value = "request.encode", havingValue = "true")
public class EncodeResponseAdvice implements ResponseBodyAdvice {
@Autowired
private ObjectMapper objectMapper;
@Override
public boolean supports(MethodParameter methodParameter, Class aClass) {
// 方法上有SecurityParameter注解的进行加密 根据需求可以在判断SecurityParameter注解中的outEncode是否为true
return methodParameter.hasMethodAnnotation(SecurityParameter.class);
}
@Override
public Object beforeBodyWrite(Object o, MethodParameter methodParameter, MediaType mediaType, Class aClass,
ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) {
try {
// 要支持接口返回string的话,response需要序列化成string对象,因为接口返回string和对象使用消息转换器不是一样的
// 把返回结果序列化成json
String dataJson = objectMapper.writeValueAsString(o);
// 进行加密返回
return EncryptionUtil.getEncodeResponse(dataJson);
} catch (JsonProcessingException e) {
log.error("json processing error", e);
}
return o;
}
}
SpringMvc的requestBody增强
// 参数进行解密的方法 request.decode配置控制是否参数进行解密(这个是RequestBody注解的增强,参数必须要加这个注解)
@Slf4j
@ControllerAdvice
@RequiredArgsConstructor
@ConditionalOnProperty(value = "request.decode", havingValue = "true")
public class DecodeRequestAdvice implements RequestBodyAdvice {
private final ObjectMapper objectMapper;
@Override
public boolean supports(MethodParameter methodParameter, Type type, Class<? extends HttpMessageConverter<?>> aClass) {
// 判断方法是否需要进行解密
return methodParameter.hasMethodAnnotation(SecurityParameter.class) && methodParameter.getMethodAnnotation(SecurityParameter.class).inDecode();
}
@Override
public HttpInputMessage beforeBodyRead(HttpInputMessage httpInputMessage, MethodParameter methodParameter, Type type, Class<? extends HttpMessageConverter<?>> aClass) throws IOException {
// 读取body对象,获取EncryptionRequest对象
InputStream body = httpInputMessage.getBody();
EncryptionRequest encryptionRequest = objectMapper.readValue(body, EncryptionRequest.class);
if (encryptionRequest.getEncryptionKey() == null) {
throw new IllegalArgumentException("encryption error,request is null");
}
// 转换成解密后的HttpInputMessage对象
HttpInputMessage decryHttpMessage = new HttpInputMessage() {
@Override
public HttpHeaders getHeaders() {
return httpInputMessage.getHeaders();
}
// 解密后的输入流
@Override
public InputStream getBody() {
return EncryptionUtil.getDecodeInputStream(encryptionRequest);
}
};
return decryHttpMessage;
}
@Override
public Object afterBodyRead(Object o, HttpInputMessage httpInputMessage, MethodParameter methodParameter, Type type, Class<? extends HttpMessageConverter<?>> aClass) {
return o;
}
@Override
public Object handleEmptyBody(Object o, HttpInputMessage httpInputMessage, MethodParameter methodParameter, Type type, Class<? extends HttpMessageConverter<?>> aClass) {
return o;
}
}
执行结果
// 测试
@RestController
public class DemoController {
// string的接口返回值要在ResponseBodyAdvice中进行特殊处理,将对象转换成string对象
// 正常业务中基本是会固定返回对象的,所以我这里就不做处理了
@SecurityParameter
@PostMapping("test")
public DemoResponse demo() {
return new DemoResponse("success");
}
}
接口执行结果