美文网首页Java 杂谈
Spring Boot基于注解实现数据加密

Spring Boot基于注解实现数据加密

作者: Wocus | 来源:发表于2019-03-04 11:47 被阅读3次

    在我们写接口的时候,有一些数据需要用到json传递,而这些数据,需要用到加密,今天讲解一篇基于Spring Boot的方法入参出参加密

    1.首先定义一个注解,以便需要加密的时候加上注解就行了

    注解的说明网上有挺多,其中需要了解一个重要的属性@Target({ElementType.METHOD,ElementType.TYPE})里面可以写多个,注解的范围,我这里标的是方法和类,可以使用注解
    接下来就是注解类的代码,定义了两个参数,入参出参是否加密,默认为都加密

    import org.springframework.web.bind.annotation.Mapping;
    
    import java.lang.annotation.*;
    
    
    @Target({ElementType.METHOD,ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    @Mapping
    @Documented
    public @interface SecurityParameter {
    
        /**
         * 入参是否解密,默认true解密
         */
        boolean inDecode() default true;
    
        /**
         * 出参是否加密,默认true加密
         */
        boolean outEncode() default true;
    }
    

    2.接下来是数据拦截进行加密

    说到加密,就有解密,加密是数据返回的时候加密传输,解密的时候,就是前端返回加密数据,后台进行解密使用,下面是关于加密(response)解密(request)的代码

    package com.sansence.house.interceptor;
    
    import com.fasterxml.jackson.databind.ObjectMapper;
    import com.sansence.house.application.SecurityParameter;
    import com.sansence.house.util.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
     * @desc 返回数据加密
     */
    @ControllerAdvice(basePackages = "com.sansence.house.controller")
    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;
        }
    }
    

    以上方法,头部注解,就是对哪个包的请求进行拦截,这里针对于接口,都进行拦截,然后进行加密,我这里使用的是DES加密算法加密

    package com.sansence.house.interceptor;
    
    import com.sansence.house.application.SecurityParameter;
    import com.sansence.house.util.DESHelper;
    import org.apache.commons.io.IOUtils;
    import org.apache.commons.lang3.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;
    
    /**
     * @desc 请求数据解密
     */
    @ControllerAdvice(basePackages = "com.sansence.house.controller")
    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;
            }
    
            public String easpString(String requestData){
                LoggerFactory.getLogger("接收加密数据").info(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 "";
            }
        }
    }
    

    以上是解密方法,其中数据加密解密可自由发挥选择自己需要的加密方式,我这里解密是使用json传递数据的,所以定义了一些规范

    3.加密方式,可自由选择,我使用的是DES加密,下面是工具类

    package com.sansence.house.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;
    import java.text.SimpleDateFormat;
    import java.time.LocalDate;
    import java.time.LocalDateTime;
    import java.time.ZoneOffset;
    import java.time.temporal.ChronoUnit;
    import java.util.Calendar;
    import java.util.Date;
    import java.util.Locale;
    
    /**
     * @author
     * @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 = "abcdef0123456789";
    
        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;
        }
    

    4.使用

    方法加上注解即可使用


    image.png

    相关文章

      网友评论

        本文标题:Spring Boot基于注解实现数据加密

        本文链接:https://www.haomeiwen.com/subject/xpvbuqtx.html