Spring Boot应用使用AES对称加密进行数据传输加密

  • 2018-05-14
  • 1,811
  • 3
  • 9

众所周知,在常见的系统应用中如果要进行数据传输,不管是否是前后分离模式!都是在前端进行参数定义+参数值进行传输!后端控制器接收请求进行处理并且返回数据!这种方式数据都是以明文方式进行传输的!安全性并不高,且各种抓包软件很容易就能抓取到其中的数据,容易被不法分子利用这种漏洞!

那么有什么办法可以对数据进行控制呢,这里我介绍两种方式!

1.对接口进行控制:

控制接口的调用,之前朋友介绍的一种方式,例如使用MD5进行一个接口加密,后端匹配这个MD5字符串,匹配成在进行调用,匹配失败就不进行调用!

这种方式也是在一定程度长进行了限制,不过也可以进行暴力破解,再次不再赘述!

2.使用AES对称加密方式进行处理:

为什么不使用非对称加密呢,因为非对称加密的字节较长,对效率有一定影响!一般都用于安全性要求较高的地方,比如支付功能!在此只介绍对称加密中的AES加密方式进行处理.

AES一般长度为16位一组,如果超出16后为下一组!整个加密后的字符串长度为16的倍数,如加密后不足16为的使用其他固定字符代替!

详见如下:AES加密CBC模式兼容互通四种编程语言平台【PHP、Javascript、Java、C#】

这种方式的流程如下:

  • 首先前后端都需要定义一个统一的key值(长度为16位),进行加解密时使用!
  • 前端将需要传输的数据进行json格式化后,使用KEY值执行加密传输到后端
  • 后端使用统一KEY值进行解密,得到加密前的json字符串
  • 拿到字符串进行解析,做相应的业务逻辑处理,这里大家都知道怎么做了,就不多说

下面贴一下本人实践代码:

定义自定义注解类:SerializedField

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
 * 返回数据加密 自定义注解
 *
 * @ClassName: SerializedField
 * @Description:
 * @author: wudengyu
 * @date: 2018/5/14 13:38
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface SerializedField {
    /**
     * 需要返回的字段
     *
     * @return
     */
    String[] includes() default {};
    /**
     * 需要去除的字段
     *
     * @return
     */
    String[] excludes() default {};
    /**
     * 数据是否需要加密
     *
     * @return
     */
    boolean encode() default true;
}

MyResponseBodyAdvice处理统一返回数据加密

import com.daibugroup.rest.admin.annotation.SerializedField;
import com.daibugroup.utils.Helper;
import org.springframework.core.MethodParameter;
import org.springframework.core.annotation.Order;
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;
import java.lang.reflect.Field;
import java.util.*;
/**
 * @ClassName: MyResponseBodyAdvice
 * @Description:
 * @author: wudengyu
 * @date: 2018/5/14 14:01
 */
@Order(1)
@ControllerAdvice(basePackages = "com.daibugroup.rest.admin.controller")
public class MyResponseBodyAdvice implements ResponseBodyAdvice {
    //包含项
    private String[] includes = {};
    //排除项
    private String[] excludes = {};
    //是否加密
    private boolean encode = true;
    @Override
    public boolean supports(MethodParameter methodParameter, Class aClass) {
        //获取当前处理请求的controller的方法
        String methodName = methodParameter.getMethod().getName();
        //获取类名称
        String className = aClass.getSimpleName();
        // 拦截/需要处理返回值 的方法
        String[] method = new String[10];
        method[0] = "list";
        return Arrays.asList(method).contains(methodName);
    }
    @Override
    public Object beforeBodyWrite(Object o, MethodParameter methodParameter, MediaType mediaType, Class aClass, ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) {
        //重新初始化为默认值
        includes = new String[]{};
        excludes = new String[]{};
        encode = true;
//        o = JSONObject.toJSONString(o).toString();
        //判断返回的对象是单个对象,还是list,活着是map
        if (o == null) {
            return null;
        }
        if (methodParameter.getMethod().isAnnotationPresent(SerializedField.class)) {
            //获取注解配置的包含和去除字段
            SerializedField serializedField = methodParameter.getMethodAnnotation(SerializedField.class);
            includes = serializedField.includes();
            excludes = serializedField.excludes();
            //是否加密
            encode = serializedField.encode();
        }
        Object retObj = null;
        if (o instanceof List) {
            //List
            List list = (List) o;
            retObj = handleList(list);
        } else {
            //Single Object
            retObj = handleSingleObject(o);
        }
        return retObj;
    }
    /**
     * 处理返回值是单个enity对象
     *
     * @param o
     * @return
     */
    private Object handleSingleObject(Object o) {
        Map<String, Object> map = new HashMap<String, Object>();
        Field[] fields = o.getClass().getDeclaredFields();
        for (Field field : fields) {
            //如果未配置表示全部的都返回
            if (includes.length == 0 && excludes.length == 0) {
                String newVal = getNewVal(o, field);
                map.put(field.getName(), newVal);
            } else if (includes.length > 0) {
                //有限考虑包含字段
                if (Helper.isStringInArray(field.getName(), includes)) {
                    String newVal = getNewVal(o, field);
                    map.put(field.getName(), newVal);
                }
            } else {
                //去除字段
                if (excludes.length > 0) {
                    if (!Helper.isStringInArray(field.getName(), excludes)) {
                        String newVal = getNewVal(o, field);
                        map.put(field.getName(), newVal);
                    }
                }
            }
        }
        return map;
    }
    /**
     * 处理返回值是列表
     *
     * @param list
     * @return
     */
    private List handleList(List list) {
        List retList = new ArrayList();
        for (Object o : list) {
            Map map = (Map) handleSingleObject(o);
            retList.add(map);
        }
        return retList;
    }
    /**
     * 获取加密后的新值
     *
     * @param o
     * @param field
     * @return
     */
    private String getNewVal(Object o, Field field) {
        String newVal = "";
        try {
            field.setAccessible(true);
            Object val = field.get(o);
            if (val != null) {
                if (encode) {
                    newVal = Helper.encode(val.toString());
                } else {
                    newVal = val.toString();
                }
            }
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        return newVal;
    }
}

AesEncryptUtil加密工具类

import org.apache.commons.codec.binary.Base64;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
public class AesEncryptUtil {
    //使用AES-128-CBC加密模式,key需要为16位,key和iv可以相同!
    private static String KEY = "dibu16614chuxing";
    private static String IV = "dibu16614chuxing";
    /**
     * 加密方法
     *
     * @param data 要加密的数据
     * @param key  加密key
     * @param iv   加密iv
     * @return 加密的结果
     * @throws Exception
     */
    public static String encrypt(String data, String key, String iv) throws Exception {
        try {
            //"算法/模式/补码方式"
            Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
            int blockSize = cipher.getBlockSize();
            byte[] dataBytes = data.getBytes();
            int plaintextLength = dataBytes.length;
            if (plaintextLength % blockSize != 0) {
                plaintextLength = plaintextLength + (blockSize - (plaintextLength % blockSize));
            }
            byte[] plaintext = new byte[plaintextLength];
            System.arraycopy(dataBytes, 0, plaintext, 0, dataBytes.length);
            SecretKeySpec keyspec = new SecretKeySpec(key.getBytes(), "AES");
            IvParameterSpec ivspec = new IvParameterSpec(iv.getBytes());
            cipher.init(Cipher.ENCRYPT_MODE, keyspec, ivspec);
            byte[] encrypted = cipher.doFinal(plaintext);
            return new Base64().encodeToString(encrypted);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }
    /**
     * 解密方法
     *
     * @param data 要解密的数据
     * @param key  解密key
     * @param iv   解密iv
     * @return 解密的结果
     * @throws Exception
     */
    public static String desEncrypt(String data, String key, String iv) throws Exception {
        try {
            byte[] encrypted1 = new Base64().decode(data);
            Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
            SecretKeySpec keyspec = new SecretKeySpec(key.getBytes(), "AES");
            IvParameterSpec ivspec = new IvParameterSpec(iv.getBytes());
            cipher.init(Cipher.DECRYPT_MODE, keyspec, ivspec);
            byte[] original = cipher.doFinal(encrypted1);
            String originalString = new String(original);
            return originalString;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }
    /**
     * 使用默认的key和iv加密
     *
     * @param data
     * @return
     * @throws Exception
     */
    public static String encrypt(String data) throws Exception {
        return encrypt(data, KEY, IV);
    }
    /**
     * 使用默认的key和iv解密
     *
     * @param data
     * @return
     * @throws Exception
     */
    public static String desEncrypt(String data) throws Exception {
        return desEncrypt(data, KEY, IV);
    }
    /**
     * 测试
     */
    public static void main(String args[]) throws Exception {
        //测试字符串
        String test = "ieBXl6AaijGAb8RCuHal9n9MDG/o/gI1XehuSYT2z/yPws1QIAaIY8zy6bE5DmKCnrXvp/ZKupy0rn66TpLtgTuTlgvYpXnv3AvLCji4OUz5cJP5nWA+IN1A5KczTKbFCNhgCZvF2X2dUwMgUiBWAmXukMrnkrnyEibCQ0kSHYeMU1QFXm71hnuu9LaanVV1uHDrX32/apj5a35K/UcOcqXuJjJfaky6FL5eG6aTrn2AiCmvm1iDlfIjZT7RGv2tPyjFdUYwCz9kmvMQ3N9sgeds3F4SOojScG9ETBNNssoHoh7WcgnCM6rndFOyaI6eiUAtKzynaeYcPwRgoczqcXg85oSgrLJpW56rMH0BVbg6PSrGRM4wLRrcarqKLMUzC9JKrnQOE2gRGg7dFez9OEY743fVvVKw==rn";
        //加密方法
//        data = encrypt(test, key, iv);
        //解密方法
        System.out.println(test.length());
        System.out.println(desEncrypt(test));
    }
}

Helper类

public class Helper {
    public static boolean isStringInArray(String str, String[] array) {
        for (String val : array) {
            if (str.equals(val)) {
                return true;
            }
        }
        return false;
    }
    public static String encode(String str) {
        String enStr = "";
        try {
            enStr = AesEncryptUtil.encrypt(str);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return enStr;
    }
    public static String decode(String str) {
        String deStr = "";
        try {
            deStr = AesEncryptUtil.desEncrypt(str);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return deStr;
    }
}

定义Aes加密过滤器AesFilter

import com.daibugroup.utils.AesEncryptUtil;
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
/**
 * @ClassName: AesFilter
 * @Description:
 */
//@WebFilter(urlPatterns = "/syslog/*")
public class AesFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
    }
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        System.out.println("自定义过滤器->doFilter");
        System.out.println("-------解密参数-------");
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        String data = request.getParameter("data");
        try {
            if (data != null) {
                //进行aes解密
                request.setAttribute("data", AesEncryptUtil.desEncrypt(data));
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        filterChain.doFilter(servletRequest, servletResponse);
    }
    @Override
    public void destroy() {
    }
}

FilterConfig过滤器配置

import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
 * @ClassName: FilterConfig
 * @Description:
 */
@Configuration
public class FilterConfig {
    @Bean
    public FilterRegistrationBean filterRegist() {
        FilterRegistrationBean frBean = new FilterRegistrationBean();
        frBean.setFilter(new AesFilter());
        //路径配置
        frBean.addUrlPatterns("/syslog/list");
        return frBean;
    }
}

最后进行调用SysLogController

以上就完成了数据传输过程中的加解密操作!

如果觉得还是不太明白的可以留下评论,本人看见就回复~

评论

发表评论