wwwframe框架中hibernate拦截器实现对数据处理

5 篇文章 0 订阅
5 篇文章 0 订阅

hibernate执行流程

    1. Hibernate 启动,Configruation 会读取并加载 Hibernate 核心配置文件和映射文件钟的信息到它实例对象中。
    2. 通过 Configuration 对象读取到的配置文件信息,创建一个 SessionFactory 对象,该对象中保存了当前数据库的配置信息、映射关系等信息。
    3. 通过 SessionFactory 对象创建一个 Session 实例,建立数据库连接。Session 主要负责执行持久化对象的增、删、改、查操作,创建一个 Session 就相当于创建一个新的数据库连接。
    4. 通过 Session 对象创建 Transaction(事务)实例对象,并开启事务。Transaction 用于事务管理,一个 Transaction 对象对应的事务可以包含多个操作。需要注意的是,Hibernate 的事务默认是关闭的,需要手动开启和关闭。
    5. Session 接口提供了各种方法,可以对实体类对象进行持久化操作(即对数据库进行操作),例如 get()、load()、save()、update()、saveOrUpdate() 等等,除此之外,Session 对象还可以创建Query 对象 或 NativeQuery 对象,分别使用 HQL 语句或原生 SQL 语句对数据库进行操作。
    6. 对实体对象持久化操作完成后,必须提交事务,若程序运行过程中遇到异常,则回滚事务。
    7. 关闭 Session 与 SessionFactory,断开与数据库的连接,释放资源。

hibernate拦截器原理

    • hibernate 读写前对数据进行处理,可以加解密、转换等数据转换
  • hibernate拦截器简介

    • Hibernate定义了一个拦截器,位于org.hibernate.Interceptor,提供了一系列的拦截器方法

      package org.hibernate;
      
      import java.io.Serializable;
      import java.util.Iterator;
      import org.hibernate.type.Type;
      
      public interface Interceptor {
          // 加载数据库时调用
          boolean onLoad(Object var1, Serializable var2, Object[] var3, String[] var4, Type[] var5) throws CallbackException;
      
          // 更新操作时调用
          boolean onFlushDirty(Object var1, Serializable var2, Object[] var3, Object[] var4, String[] var5, Type[] var6) throws CallbackException;
      
          // 添加操作时调用
          boolean onSave(Object var1, Serializable var2, Object[] var3, String[] var4, Type[] var5) throws CallbackException;
      
          // 其他方法省略...
      }
      
    • 但如果直接实现Interceptor,我们还需要实现该接口下面的所有方法,为此Hibernate为我们提供了空拦截器EmptyInterceptor。EmptyInterceptor空拦截器继承自Interceptor拦截器,已经帮我们实现接口内所有的方法,这样就不需要实现所有接口方法了。我们可以定义一个类去继承空拦截器,根据需要去重写空拦截器里面提供的方法。

      package org.hibernate;
      
      import java.io.Serializable;
      import java.util.Iterator;
      import org.hibernate.type.Type;
      
      public class EmptyInterceptor implements Interceptor, Serializable {
          public static final Interceptor INSTANCE = new EmptyInterceptor();
      
          protected EmptyInterceptor() {
          }
      
          public void onDelete(Object entity, Serializable id, Object[] state, String[] propertyNames, Type[] types) {
          }
      
          public boolean onFlushDirty(Object entity, Serializable id, Object[] currentState, Object[] previousState, String[] propertyNames, Type[] types) {
              return false;
          }
      
          // 其他方法省略...
      
      }
      
  • 常用方法
序号:方法:描述:
1onLoad在初始化对象之前调用。拦截器可能会更改状态,该状态将被传播到持久对象。请注意,当调用此方法时,实体将是该类的一个未初始化的空实例
2onSave在保存对象之前调用。拦截器可以修改状态,该状态将用于SQL插入并传播到持久对象
3onFlushDirty在冲洗过程中检测到对象变脏时调用。拦截器可以修改检测到的currentState,它将被传播到数据库和持久对象。请注意,并非所有刷新都以与数据库的实际同步结束,在这种情况下,新的currentState将传播到对象,但不一定(立即)传播到数据库。强烈建议拦截器不要修改以前的状态

实例

  • 实现思路
    1. 敏感实体类上添加加密注解,可以通过注解区分出哪些实体类需要加密
    2. 敏感实体类的字段也需要增加加密注解,用于区分哪些字段需要加密
    3. 利用在Hibernate的拦截器EmptyInterceptor的对应事件,通过反射获取需要处理的实体类和字段,在数据入库前在拦截器对应的方法对数据进行加密处理,在数据读取时在拦截器中对数据进行解密处理
  • 实体注解

    import java.lang.annotation.*;
    
    /**
     * 需要加解密的表注解,只有添加此注解的表才需要进行加解密
     */
    @Target(ElementType.TYPE)
    @Documented
    @Retention(RetentionPolicy.RUNTIME)
    public @interface EncryptTable {
    
    }
    
  • 属性注解

    package com.sitech.interceptor;
    
    import java.lang.annotation.*;
    
    /**
     * 加解密表字段,只有添加了此注解的实体类字段才要进行加解密
     */
    @Documented
    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.FIELD)
    public @interface EncryptField {
    
    }
    
  • 加密工具

    import javax.crypto.*;
    import javax.crypto.spec.SecretKeySpec;
    import java.io.UnsupportedEncodingException;
    import java.security.InvalidKeyException;
    import java.security.NoSuchAlgorithmException;
    import java.security.SecureRandom;
    
    public class AESUtils {
        public static final String KEYWORD_BJDX = "BJDX";
    
        public static byte[] encrypt(String content, String keyWord) {
            try {
                KeyGenerator kgen = KeyGenerator.getInstance("AES");
                SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG");//指定算法名称(SHA1PRNG),生成SecureRandom 对象
                secureRandom.setSeed(keyWord.getBytes());
                kgen.init(128, secureRandom);
                SecretKey secretKey = kgen.generateKey();
                byte[] enCodeFormat = secretKey.getEncoded();
                SecretKeySpec key = new SecretKeySpec(enCodeFormat, "AES");
                Cipher cipher = Cipher.getInstance("AES");// 创建密码器
                byte[] byteContent = content.getBytes("utf-8");
                cipher.init(Cipher.ENCRYPT_MODE, key);// 初始化
                byte[] result = cipher.doFinal(byteContent);
                return result; // 加密
            } catch (NoSuchAlgorithmException e) {
                e.printStackTrace();
            } catch (NoSuchPaddingException e) {
                e.printStackTrace();
            } catch (InvalidKeyException e) {
                e.printStackTrace();
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            } catch (IllegalBlockSizeException e) {
                e.printStackTrace();
            } catch (BadPaddingException e) {
                e.printStackTrace();
            }
            return null;
        }
    
        public static String encryptToHex(String content, String password) {
            return parseByte2HexStr(encrypt(content, password));
        }
    
        public static byte[] decrypt(byte[] content, String keyWord) {
            try {
                KeyGenerator kgen = KeyGenerator.getInstance("AES");
                SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG");
                secureRandom.setSeed(keyWord.getBytes());
                kgen.init(128, secureRandom);
                SecretKey secretKey = kgen.generateKey();
                byte[] enCodeFormat = secretKey.getEncoded();
                SecretKeySpec key = new SecretKeySpec(enCodeFormat, "AES");
                Cipher cipher = Cipher.getInstance("AES");// 创建密码器
                cipher.init(Cipher.DECRYPT_MODE, key);// 初始化
                byte[] result = cipher.doFinal(content);
                return result; // 加密
            } catch (NoSuchAlgorithmException e) {
                e.printStackTrace();
            } catch (NoSuchPaddingException e) {
                e.printStackTrace();
            } catch (InvalidKeyException e) {
                e.printStackTrace();
            } catch (IllegalBlockSizeException e) {
                e.printStackTrace();
            } catch (BadPaddingException e) {
                e.printStackTrace();
            }
            return null;
        }
    
        public static byte[] decryptHex(String content, String keyWord) {
            return decrypt(parseHexStr2Byte(content), keyWord);
        }
    
        public static String parseByte2HexStr(byte buf[]) {
            StringBuffer sb = new StringBuffer();
            for (int i = 0; i < buf.length; i++) {
                String hex = Integer.toHexString(buf[i] & 0xFF);
                if (hex.length() == 1) {
                    hex = '0' + hex;
                }
                sb.append(hex.toUpperCase());
            }
            return sb.toString();
        }
    
        public static byte[] parseHexStr2Byte(String hexString) {
            if (hexString.length() < 1) return null;
            byte[] byteArray = new byte[hexString.length() / 2];
            byte tmpByte;
            for (int i = 0; i < hexString.length(); i = i + 2) {
                tmpByte = new Integer(Integer.parseInt(hexString.substring(i, i + 2), 16)).byteValue();
                byteArray[i / 2] = tmpByte;
            }
            return byteArray;
        }
    
        public static boolean isAesEnStrHes(String valuse) {
            boolean isAes = false;
            try {
                byte[] bytes = decryptHex(valuse, KEYWORD_BJDX);
                if (bytes == null) {
                    isAes = false;
                } else {
                    isAes = true;
                }
            } catch (Exception e) {
                isAes = false;
            }
    
            return isAes;
        }
    
        public static void main(String[] args) {
    //        String en_1 = encryptToHex("北北北北北北北北北北北北北北北北北北北北", KEYWORD_BJDX);
    //        System.out.println(en_1);
    //        System.out.println(en_1.length());
    //        try {
    //            System.out.println(new String(decryptHex(en_1, KEYWORD_BJDX), "UTF-8"));
    //        } catch (UnsupportedEncodingException e) {
    //            e.printStackTrace();
    //        }
    
            System.out.println(AESUtils.isAesEnStrHes("北京"));
            System.out.println(AESUtils.isAesEnStrHes("ad"));
            System.out.println(AESUtils.isAesEnStrHes("3BE2C4D4CF9B5980AA312D1966255D1E1DB9959BE613BCDAB7F2469FDF8F147CEE4374B53DB51955593F6D96365E3EF8CB0564A04E49218219C18AE65FF6F4028B4CC97C829ABBFC08443D95DCBF74DD105CC6717863C8DC35ABC158A8FC8E9604E61AD9B01AA598BBAA596D90421C66FFD4CAB9036B15E9F4E3C483466B707E48DD712876FE41DE1D0A584D4153A712"));
        }
    }
    
  • 拦截器

    import org.hibernate.EmptyInterceptor;
    import org.hibernate.type.Type;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.stereotype.Component;
    
    import java.io.Serializable;
    import java.lang.reflect.Field;
    import java.util.ArrayList;
    import java.util.Collections;
    import java.util.List;
    import java.util.Objects;
    
    @Component
    public class EncryptInterceptor extends EmptyInterceptor {
    
        private final static Logger LOGGER = LoggerFactory.getLogger(EncryptInterceptor.class);
    
        /**
         * 更新时调用
         *
         * @param entity        实体类
         * @param id            主键
         * @param currentState  当前实体类对应的值
         * @param previousState 修改前实体类对应的值
         * @param propertyNames 字段名
         * @param types         实体类每个属性类型对应hibernate的类型
         * @return true | false true才会修改数据
         */
        @Override
        public boolean onFlushDirty(Object entity, Serializable id, Object[] currentState, Object[] previousState, String[] propertyNames, Type[] types) {
            System.out.println("QQQQQQQQQQQQQQQQQQ=================onFlushDirty【IN】");
            Object[] newState = dealField(entity, currentState, propertyNames, "onFlushDirty");
            return super.onFlushDirty(entity, id, newState, previousState, propertyNames, types);
        }
    
        /**
         * 加载时调用
         *
         * @param entity        实体类
         * @param id            主键
         * @param state         实体类对应的值
         * @param propertyNames 字段名
         * @param types         实体类每个属性类型对应hibernate的类型
         * @return true | false true才会修改数据
         */
        @Override
        public boolean onLoad(Object entity, Serializable id, Object[] state, String[] propertyNames, Type[] types) {
            System.out.println("QQQQQQQQQQQQQQQQQQ=================onLoad【IN】");
            Object[] newState = dealField(entity, state, propertyNames, "onLoad");
            return super.onLoad(entity, id, newState, propertyNames, types);
        }
    
        /**
         * 保存时调用
         *
         * @param entity        实体类
         * @param id            主键
         * @param state         实体类对应的值
         * @param propertyNames 字段名
         * @param types         实体类每个属性类型对应hibernate的类型
         * @return true | false true才会修改数据
         */
        @Override
        public boolean onSave(Object entity, Serializable id, Object[] state, String[] propertyNames, Type[] types) {
            System.out.println("QQQQQQQQQQQQQQQQQQ=================onSave【IN】");
            Object[] newState = dealField(entity, state, propertyNames, "onSave");
            return super.onSave(entity, id, newState, propertyNames, types);
        }
    
    
        /**
         * 处理字段对应的数据
         *
         * @param entity        实体类
         * @param state         数据
         * @param propertyNames 字段名称
         * @return 解密后的字段名称
         */
        private Object[] dealField(Object entity, Object[] state, String[] propertyNames, String type) {
            List<String> annotationFields = getAnnotationField(entity);
            LOGGER.info("调用方法:{}, 需要加密的字段:{}", type, annotationFields);
            System.out.println("================调用方法:{}, 需要加密的字段:{}" + type + annotationFields);
            // 遍历字段名和加解密字段名
            for (String aField : annotationFields) {
                for (int i = 0; i < propertyNames.length; i++) {
                    if (!propertyNames[i].equals(aField)) {
                        continue;
                    }
                    // 如果字段名和加解密字段名对应且不为null或空
                    if (state[i] == null || Objects.equals(state[i].toString(), "")) {
                        continue;
                    }
                    if ("onSave".equals(type) || "onFlushDirty".equals(type)) {
                        if (!AESUtils.isAesEnStrHes(state[i].toString())) {
                            LOGGER.info("当前字段:{}, 加密前:{}", aField, state[i]);
                            state[i] = AESUtils.encryptToHex(state[i].toString(), AESUtils.KEYWORD_BJDX);
                            LOGGER.info("当前字段:{}, 加密后:{}", aField, state[i]);
                        }
                    } else if ("onLoad".equals(type)) {
                        if (AESUtils.isAesEnStrHes(state[i].toString())) {
                            LOGGER.info("当前字段:{}, 解密前:{}", aField, state[i]);
                            state[i] = new String(AESUtils.decryptHex(state[i].toString(), AESUtils.KEYWORD_BJDX));
                            LOGGER.info("当前字段:{}, 解密后:{}", aField, state[i]);
                        }
                    }
                }
            }
            return state;
        }
    
    
        /**
         * 获取实体类中带有注解EncryptField的变量名
         *
         * @param entity 实体类
         * @return 需要加解密的字段
         */
        private List<String> getAnnotationField(Object entity) {
            // 判断当前实体类是否有加解密注解
            Class<?> entityClass = entity.getClass();
            System.out.println("================entityClass" + entityClass);
            if (!entityClass.isAnnotationPresent(EncryptTable.class)) {
                return Collections.emptyList();
            }
            List<String> fields = new ArrayList<>();
            // 获取实体类下的所有成员并判断是否存在加解密注解
            Field[] declaredFields = entityClass.getDeclaredFields();
            for (Field field : declaredFields) {
                EncryptField encryptField = field.getAnnotation(EncryptField.class);
                if (Objects.isNull(encryptField)) {
                    continue;
                }
                fields.add(field.getName());
                System.out.println("================entityClass" + entityClass);
            }
            return fields;
        }
    
    }
    
  • hibernate拦截器配置
    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:context="http://www.springframework.org/schema/context"
           xmlns:aop="http://www.springframework.org/schema/aop"
           xmlns:tx="http://www.springframework.org/schema/tx"
           xsi:schemaLocation="http://www.springframework.org/schema/beans
               http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
               http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd
               http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
               http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd">
        ...
        <bean id="myEntityInterceptor" class="com.sitech.interceptor.EncryptInterceptor"/>
        ...
        <bean id="sessionFactory"
            class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
            ...
            <property name="entityInterceptor" ref="myEntityInterceptor"></property>
            ...
        </bean>
        ...
        <context:annotation-config/>
    </beans>
    
  • 注意事项
    • 1.数据处理注意形如null空的判断
    • 2.hibernate有缓存配置的时候注意取和写数据的原始数据的判断和处理
    • 3.使用sql串执行时观察是否该方案还适用
    • 其他
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

乘风御浪云帆之上

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值