Spring Boot JSON 加解密

Spring Boot JSON 加解密

业务场景

在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;
}

JSON - 解密

@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 "";
        }
    }
}

JSON - 加密

@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】

猜你喜欢

转载自blog.csdn.net/Cy_LightBule/article/details/87866333