在Controller前对所有请求中的加密JSON进行解密。
在响应给调用方时为所有响应数据进行加密。
使用RequestBodyAdvice和ResponseBodyAdvice
RequestBodyAdvice
请求拦截并加密
ResponseBodyAdvice
响应拦截并加密
请求必须是@RequestBody、请求的Content-Type=application/x-www-form-urlencoded
public String index(@RequestBody String body){
logger.info("请求信息: " + body);
return body;
}
@ControllerAdvice
public class MyRequestBodyAdvice implements RequestBodyAdvice {
private static final Logger logger = LoggerFactory.getLogger(MyRequestBodyAdvice.class);
private static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8");
//是否解密
private static boolean ENCODE = true;
@Override
public boolean supports(MethodParameter methodParameter, Type type, Class<? extends HttpMessageConverter<?>> aClass) {
return true;
}
@Override
public Object handleEmptyBody(Object body, HttpInputMessage httpInputMessage, MethodParameter methodParameter, Type type, Class<? extends HttpMessageConverter<?>> aClass) {
return body;
}
@Override
public HttpInputMessage beforeBodyRead(HttpInputMessage inputMessage, MethodParameter methodParameter, Type type, Class<? extends HttpMessageConverter<?>> aClass) throws IOException {
try {
if (methodParameter.getMethod().isAnnotationPresent(SecurityParameter.class)) {
//获取注解配置的包含和去除字段
SecurityParameter serializedField = methodParameter.getMethodAnnotation(SecurityParameter.class);
//入参是否需要解密
ENCODE = serializedField.inDecode();
}
logger.info("对方法method :【" + methodParameter.getMethod().getName() + "】返回数据进行解密");
return new MyHttpInputMessage(inputMessage);
} catch (Exception e) {
e.printStackTrace();
logger.error("对方法method :【" + methodParameter.getMethod().getName() + "】返回数据进行解密出现异常:"+e.getMessage());
return inputMessage;
}
}
@Override
public Object afterBodyRead(Object body, HttpInputMessage httpInputMessage, MethodParameter methodParameter, Type type, Class<? extends HttpMessageConverter<?>> aClass) {
return body;
}
class MyHttpInputMessage implements HttpInputMessage {
private HttpHeaders headers;
private InputStream body;
public MyHttpInputMessage(HttpInputMessage inputMessage) throws Exception {
this.headers = inputMessage.getHeaders();
String bodyMessage = FileCopyUtils.copyToString(new InputStreamReader(inputMessage.getBody(), "utf-8"));
if (ENCODE) {
this.body = IOUtils.toInputStream(DESHelper.decrypt(easpString(URLDecoder.decode(bodyMessage, "utf-8"))));
} else {
this.body = IOUtils.toInputStream(easpString(URLDecoder.decode(bodyMessage, "utf-8")));
}
}
private Charset getContentTypeCharset(MediaType contentType) {
if (contentType != null && contentType.getCharset() != null) {
return contentType.getCharset();
} else {
return DEFAULT_CHARSET;
}
}
@Override
public InputStream getBody() throws IOException {
return body;
}
@Override
public HttpHeaders getHeaders() {
return headers;
}
/**
* @description:迭代格式化加密JSON串
* @version 1.0
* @date: 2019/2/21 下午6:28
* @mofified By:
*/
public String easpString(String requestData){
if(requestData != null && !requestData.equals("")){
if(!requestData.startsWith("value=")){
throw new RuntimeException("参数【requestData】缺失异常!");
}else{
int closeLen = requestData.length();
int openLen = "value=".length();
return StringUtils.substring(requestData,openLen,closeLen);
}
}
return "";
}
}
}
@ControllerAdvice
public class MyResponseBodyAdvice implements ResponseBodyAdvice {
private final static Logger logger = LoggerFactory.getLogger(MyResponseBodyAdvice.class);
//是否解密
private static boolean encode = true;
@Override
public boolean supports(MethodParameter methodParameter, Class aClass) {
return true;
}
@Override
public Object beforeBodyWrite(Object body, MethodParameter methodParameter, MediaType mediaType, Class aClass, ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) {
if (methodParameter.getMethod().isAnnotationPresent(SecurityParameter.class)) {
//获取注解配置的包含和去除字段
SecurityParameter serializedField = methodParameter.getMethodAnnotation(SecurityParameter.class);
//出参是否需要加密
encode = serializedField.outEncode();
}
if (encode) {
logger.info("对方法method :【" + methodParameter.getMethod().getName() + "】返回数据进行加密");
ObjectMapper objectMapper = new ObjectMapper();
try {
String result = objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(body);
return DESHelper.encrypt(result);
} catch (Exception e) {
e.printStackTrace();
logger.error("对方法method :【" + methodParameter.getMethod().getName() + "】返回数据进行解密出现异常:"+e.getMessage());
}
}
return body;
}
}
package com.sb.ed.util;
import org.apache.tomcat.util.codec.binary.Base64;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESKeySpec;
import java.security.SecureRandom;
/**
* @version 1.0
* @description:
* @date: 2019/2/21 下午1:54
* @mofified By:
* DES加密介绍
* DES是一种对称加密算法,所谓对称加密算法即:加密和解密使用相同密钥的算法。DES加密算法出自IBM的研究,
* 后来被美国政府正式采用,之后开始广泛流传,但是近些年使用越来越少,因为DES使用56位密钥,以现代计算能力,
* 24小时内即可被破解。虽然如此,在某些简单应用中,我们还是可以使用DES加密算法,本文简单讲解DES的JAVA实现。
* 注意:DES加密和解密过程中,密钥长度都必须是8的倍数
*
*/
public class DESHelper {
private final static String DES = "DES";
private final static String KEY = "www.com.cn";
public DESHelper() {
}
public static String encrypt(String pliantext) throws Exception {
return encodeBase64(encryptDES(pliantext,KEY));
}
public static String encrypt(String pliantext,String key) throws Exception {
return encodeBase64(encryptDES(pliantext,key));
}
public static String decrypt(String ciphertext) throws Exception{
return decryptDES(decodeBase64(ciphertext.getBytes()),KEY);
}
public static String decrypt(String ciphertext, String key) throws Exception {
return decryptDES(decodeBase64(ciphertext.getBytes()), key);
}
/**
* base64编码
* @param binaryData
* @return
* @throws Exception
*/
private static String encodeBase64(byte[] binaryData)throws Exception{
try{
return Base64.encodeBase64String(binaryData);
}catch (Exception e){
e.printStackTrace();
throw new RuntimeException("BASE64编码失败!");
}
}
/**
* Base64解码
* @param binaryData
* @return
*/
private static byte[] decodeBase64(byte[] binaryData){
try {
return Base64.decodeBase64(binaryData);
}catch (Exception e){
e.printStackTrace();
throw new RuntimeException("BASE64解码失败!");
}
}
public static byte[] encryptDES(String data, String key){
try {
// 生成一个可信任的随机数源 , SHA1PRNG: 仅指定算法名称
SecureRandom random = SecureRandom.getInstance("SHA1PRNG");
// 从原始密钥数据创建DESKeySpec对象
DESKeySpec deskey = new DESKeySpec(key.getBytes("UTF-8"));
//创建一个密匙工厂,然后用它把DESKeySpec转换成
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(DES);
SecretKey secretKey = keyFactory.generateSecret(deskey);
//Cipher对象实际完成加密操作
Cipher cipher = Cipher.getInstance(DES);
//用密匙初始化Cipher对象,
cipher.init(Cipher.ENCRYPT_MODE,secretKey,random);
//现在,获取数据并加密
//正式执行加密操作
return cipher.doFinal(data.getBytes("UTF-8"));
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
public static String decryptDES(byte[] data ,String key){
try {
// 算法要求有一个可信任的随机数源, SHA1PRNG: 仅指定算法名称
SecureRandom random = SecureRandom.getInstance("SHA1PRNG");
// 创建一个DESKeySpec对象
DESKeySpec desKeySpec = new DESKeySpec(key.getBytes("UTF-8"));
// 创建一个密匙工厂
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(DES);
// 将DESKeySpec对象转换成SecretKey对象
SecretKey secretKey = keyFactory.generateSecret(desKeySpec);
// Cipher对象实际完成解密操作
Cipher cipher = Cipher.getInstance(DES);
// 用密匙初始化Cipher对象
cipher.init(Cipher.DECRYPT_MODE,secretKey,random);
// 真正开始解密操作
return new String(cipher.doFinal(data),"UTF-8");
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
public static void main(String[] args) throws Exception {
String enString = encrypt("\"cDes\":\"描述信息\",\"cCode\":\"9521\",\"cName\":\"Frank-New");
System.out.println("加密后的字串是:" + enString);
String deString = decrypt(enString);
System.out.println("解密后的字串是:" + deString);
}
}
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Mapping
@Documented
public @interface SecurityParameter {
boolean inDecode() default true;
boolean outEncode() default true;
}
spring-boot-encrypt-decode【GItHub】