Spring Boot开发基于restful类型的API,我们再处理JSON请求时通常使用@RequestBody和@ResponseBody注解,
针对HTTP JSON请求需要解密和返回的JSON数据我们经常需要对数据进行加密,有的时候我们还必须过滤掉一些对象字段的值来减少网络流量。
对HTTP JSON请求数据解密 和 对返回的JSON数据进行加密,Spring提供了RequestBodyAdvice和ResponseBodyAdvice两个接口 供开发者实现。
maven项目依赖
UTF-8 1.8 org.springframework.boot spring-boot-starter-parent 1.5.8.RELEASE org.springframework.boot spring-boot-starter-web org.apache.commons commons-lang3 3.4 commons-io commons-io 2.4
加解密工具类:
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; /** * @author huang_guoqiang * @desc DES加解密 * @date 2017/11/14 11:38 * * 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("{\"pageEntity\":{\"pageSize\":\"20\",\"pageIndex\":\"0\"},\"instance\":{\"status\":\"7\"}}"); System.out.println("加密后的字串是:" + enString); String deString = decrypt(enString); System.out.println("解密后的字串是:" + deString); } }
注解类:
import org.springframework.web.bind.annotation.Mapping; import java.lang.annotation.*; /** * @author huang_guoqiang * @desc 注解 * @date 2018/3/2 11:03 */ @Target({ElementType.METHOD,ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Mapping @Documented public @interface SecurityParameter { /** * 入参是否解密,默认解密 */ boolean inDecode() default true; /** * 出参是否加密,默认加密 */ boolean outEncode() default true; }
import com.ykc.api.SecurityParameter;
import com.ykc.utils.DESHelper; import org.apache.commons.io.IOUtils; import org.apache.commons.lang.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; 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.RequestBodyAdvice; import java.io.IOException; import java.io.InputStream; import java.lang.reflect.Type; /** * @author huang_guoqiang * @desc 请求数据解密 * @date 2018/3/2 11:01 */ @ControllerAdvice(basePackages = "com.ykc.bill.action") public class MyRequestBodyAdvice implements RequestBodyAdvice { private static final Logger logger = LoggerFactory.getLogger(MyRequestBodyAdvice.class); @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 { boolean encode = false; if (methodParameter.getMethod().isAnnotationPresent(SecurityParameter.class)) { //获取注解配置的包含和去除字段 SecurityParameter serializedField = methodParameter.getMethodAnnotation(SecurityParameter.class); //入参是否需要解密 encode = serializedField.inDecode(); } if (encode) { logger.info("对方法method :【" + methodParameter.getMethod().getName() + "】返回数据进行解密"); return new MyHttpInputMessage(inputMessage); }else{ return 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(); this.body = IOUtils.toInputStream(DESHelper.decrypt(easpString(IOUtils.toString(inputMessage.getBody(), "UTF-8"))), "UTF-8"); } @Override public InputStream getBody() throws IOException { return body; } @Override public HttpHeaders getHeaders() { return headers; } /** * * @param requestData * @return */ public String easpString(String requestData){ if(requestData != null && !requestData.equals("")){ if(!requestData.startsWith("{\"requestData\":")){ throw new RuntimeException("参数【requestData】缺失异常!"); }else{ int closeLen = requestData.length()-2; int openLen = "{\"requestData\":".length()+1; return StringUtils.substring(requestData,openLen,closeLen); } } return ""; } } }
import com.fasterxml.jackson.databind.ObjectMapper; import com.ykc.api.SecurityParameter; import com.ykc.utils.DESHelper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.core.MethodParameter; import org.springframework.http.MediaType; 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; /** * @author huang_guoqiang * @desc 返回数据加密 * @date 2018/3/2 11:12 */ @ControllerAdvice(basePackages = "com.ykc.bill.action") public class MyResponseBodyAdvice implements ResponseBodyAdvice { private final static Logger logger = LoggerFactory.getLogger(MyResponseBodyAdvice.class); @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) { boolean encode = false; 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; } }