zoukankan      html  css  js  c++  java
  • java实现MySQL数据加密存储---自定义注解+自定义mybatis拦截器

    思路  调用拦截器 实现加解密

    通过自定义加解密拦截器  判断是否是 加解密实体类 的字段   调用 加解密处理类 执行具体算法加解密

    自定义类注解 

    /**
     * 注解敏感信息类的注解
     */
    @Inherited
    @Target({ ElementType.TYPE })
    @Retention(RetentionPolicy.RUNTIME)
    public @interface SensitiveData {
    }
    View Code

    自定义字段注解

    /**
     * 注解敏感信息类中敏感字段的注解
     */
    @Inherited
    @Target({ ElementType.FIELD })
    @Retention(RetentionPolicy.RUNTIME)
    public @interface SensitiveField {
    }
    View Code

    实体类加上  自定义类注解    字段加上 自定义字段注解

    @SensitiveData
    @TableName(value ="vip_card")
    @Data
    public class VipCardVO implements Serializable {
        /**
         * 主键
         */
        @TableId(type = IdType.AUTO)
        private Integer id;
    
        /**
         * 卡号
         */
        @SensitiveField
        private String cardNo;
    
        /**
         * 用户名
         */
        @SensitiveField
        private String name;
    
        /**
         * 性别
         */
        private Integer gender;
    
        /**
         * 年龄
         */
        private Integer age;
    
        /**
         * 邮箱
         */
        private String email;
    
        /**
         * 身份证号
         */
    
        private String idNumber;
    
        /**
         * 地址
         */
    
        private String address;
    
        /**
         * 手机号
         */
    
        private String phoneNumber;
    
        /**
         * 创建时间
         */
        @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone="Asia/Shanghai")
        private Date createTime;
    
        /**
         * 更新时间
         */
        @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone="Asia/Shanghai")
        private Date updateTime;
    }
    View Code

    加解密处理类

    @Component
    public class AEScrypto {
    
        private final static String password = "V********=";
    
        /**
         * 加密
         *
         * @param declaredFields paramsObject所声明的字段
         * @param paramsObject   mapper中paramsType的实例
         * @return T
         * @throws IllegalAccessException 字段不可访问异常
         */
        public static <T> T encrypt(Field[] declaredFields, T paramsObject) throws IllegalAccessException {
            for (Field field : declaredFields) {
                //取出所有被EncryptDecryptField注解的字段
                SensitiveField sensitiveField = field.getAnnotation(SensitiveField.class);
                if (!Objects.isNull(sensitiveField)) {
                    field.setAccessible(true);
                    Object object = field.get(paramsObject);
                    //暂时只实现String类型的加密
                    if (object instanceof String) {
                        String value = (String) object;
                        //加密  这里我使用自定义的AES加密工具
                        field.set(paramsObject, AESUtil.encrypt(value,password));
                    }
                }
            }
            return paramsObject;
        }
        /**
         * 解密
         *
         * @param result resultType的实例
         * @return T
         * @throws IllegalAccessException 字段不可访问异常
         */
        public static <T> T decrypt(T result) throws IllegalAccessException {
            //取出resultType的类
            Class<?> resultClass = result.getClass();
            Field[] declaredFields = resultClass.getDeclaredFields();
            for (Field field : declaredFields) {
                //取出所有被EncryptDecryptField注解的字段
                SensitiveField sensitiveField = field.getAnnotation(SensitiveField.class);
                if (!Objects.isNull(sensitiveField)) {
                    field.setAccessible(true);
                    Object object = field.get(result);
                    //只支持String的解密
                    if (object instanceof String) {
                        String value = (String) object;
                        //对注解的字段进行逐一解密
                        field.set(result, AESUtil.decrypt(value,password));
                    }
                }
            }
            return result;
        }
    }
    View Code

    加解密工具算法工具类

    public class AESUtil {
    
        private static final String KEY_ALGORITHM = "AES";
        private static final String DEFAULT_CIPHER_ALGORITHM = "AES/ECB/PKCS5Padding";//默认的加密算
    
        /**
         * AES 加密操作
         *
         * @param content
         * @param password
         * @return
         */
        public static String encrypt(String content, String password) {
            try {
                Cipher cipher = Cipher.getInstance(DEFAULT_CIPHER_ALGORITHM);
    
                byte[] byteContent = content.getBytes("utf-8");
    
                cipher.init(Cipher.ENCRYPT_MODE, getSecretKey(password));// 初始化为加密模式的密码器
    
                byte[] result = cipher.doFinal(byteContent);// 加密
    
    
                String encode = Base64.getEncoder().encodeToString(result);
                //encode.
                return encode;//通过Base64转码返回
            } catch (Exception ex) {
                Logger.getLogger(AESUtil.class.getName()).log(Level.SEVERE, null, ex);
            }
    
            return null;
        }
    
        /**
         * AES 解密操作
         *
         * @param content
         * @param password
         * @return
         */
        public static String decrypt(String content, String password) {
    
            try {
                Cipher cipher = Cipher.getInstance(DEFAULT_CIPHER_ALGORITHM);
    
                cipher.init(Cipher.DECRYPT_MODE, getSecretKey(password));
    
                //执行操作
                byte[] result = cipher.doFinal(Base64.getDecoder().decode(content));
    
                return new String(result, "utf-8");
            } catch (Exception ex) {
    //            log.error(ex.getMessage(),ex);
            }
    
            return null;
        }
    }
    View Code

    加密拦截器 实现mybatis 拦截器 接口

    @Component
    @Intercepts({
            @Signature(type = ParameterHandler.class, method = "setParameters", args = PreparedStatement.class),
    })
    public class EncryptInterceptor implements Interceptor {
    
        @Override
        public Object intercept(Invocation invocation) throws Throwable {
            //@Signature 指定了 type= parameterHandler 后,这里的 invocation.getTarget() 便是parameterHandler 
            //若指定ResultSetHandler ,这里则能强转为ResultSetHandler
            ParameterHandler parameterHandler = (ParameterHandler) invocation.getTarget();
            // 获取参数对像,即 mapper 中 paramsType 的实例
            Field parameterField = parameterHandler.getClass().getDeclaredField("parameterObject");
            parameterField.setAccessible(true);
    
            //取出实例
            Object parameterObject = parameterField.get(parameterHandler);
            if (parameterObject != null) {
                Class<?> parameterObjectClass = parameterObject.getClass();
                //校验该实例的类是否被@SensitiveData所注解
                SensitiveData sensitiveData = AnnotationUtils.findAnnotation(parameterObjectClass, SensitiveData.class);
                if (Objects.nonNull(sensitiveData)) {
                    //取出当前当前类所有字段,传入加密方法
                    Field[] declaredFields = parameterObjectClass.getDeclaredFields();
                    AEScrypto.encrypt(declaredFields, parameterObject);
                }
    
            }
            return invocation.proceed();
        }
     
        /**
         * 切记配置,否则当前拦截器不会加入拦截器链
         */
        @Override
        public Object plugin(Object o) {
            return Plugin.wrap(o, this);
        }
     
        /**
         * 自定义配置写入,没有自定义配置的可以直接置空此方法
         */
        @Override
        public void setProperties(Properties properties) {
        }
    }
    View Code

    解密拦截器 实现mybatis 拦截器 接口

    @Component
    @Intercepts({
            @Signature(type = ResultSetHandler.class, method = "handleResultSets", args = {Statement.class})
    })
    public class DecryptInterceptor implements Interceptor {
     
    
        @Override
        public Object intercept(Invocation invocation) throws Throwable {
            //取出查询的结果
            Object resultObject = invocation.proceed();
            if (Objects.isNull(resultObject)) {
                return null;
            }
            //基于selectList
            if (resultObject instanceof ArrayList) {
                ArrayList resultList = (ArrayList) resultObject;
                if (!CollectionUtils.isEmpty(resultList) && needToDecrypt(resultList.get(0))) {
                    for (Object result : resultList) {
                        //逐一解密
                        AEScrypto.decrypt(result);
                    }
                }
            //基于selectOne
            } else {
                if (needToDecrypt(resultObject)) {
                    AEScrypto.decrypt(resultObject);
                }
            }
            return resultObject;
        }
     
        private boolean needToDecrypt(Object object) {
            Class<?> objectClass = object.getClass();
            SensitiveData sensitiveData = AnnotationUtils.findAnnotation(objectClass, SensitiveData.class);
            return Objects.nonNull(sensitiveData);
        }
     
     
        @Override
        public Object plugin(Object target) {
            return Plugin.wrap(target, this);
        }
     
        @Override
        public void setProperties(Properties properties) {
     
        }
    }
    View Code
    古人学问无遗力,少壮工夫老始成。 纸上得来终觉浅,绝知此事要躬行。
  • 相关阅读:
    解决ListView异步加载数据之后不能点击的问题
    android点击实现图片放大缩小 java技术博客
    关于 数据文件自增长 的一点理解
    RAC 实例不能启动 ORA1589 signalled during ALTER DATABASE OPEN
    Linux 超级用户的权利
    RAC 实例 迁移到 单实例 使用导出导入
    Shell 基本语法
    Linux 开机引导与关机过程
    RAC 实例不能启动 ORA1589 signalled during ALTER DATABASE OPEN
    Oracle RAC + Data Guard 环境搭建
  • 原文地址:https://www.cnblogs.com/wf-zhang/p/14939638.html
Copyright © 2011-2022 走看看