当前位置: 首页 > article >正文

SpringDataJpa-字段加解密存储

SpringDataJpa-字段加解密存储

    • 背景
    • 场景
    • 实现
      • 类型转换器
        • 实体类修改
      • Crypto 注解
      • AOP
    • 目前可使用场景
    • 注意

背景

遇到一个需求,对数据库中的某些字段进行加密存储,但是在各个服务流转中,需要是解密状态的。框架使用的是JPA。

Spring 提供了 AttributeConverter<T,R> 用来将实体属性与数据库字段之间的逻辑转换。换日期格式转换这种。但是我们这边需要改变类型,只需要改变类型的值。

但是使用原生SQL的时候用加密字段进行查询的化,是不会使用类型转换器的。所以我这里增加了一个注解,来拦截标有该注解的参数,然后将参数的值进行修改,然后再进行查询。这里实现的化使用的是 AOP

场景

  • 使用实体类进行查询,插入,更新,删除(HQL
    • 使用加密字段进行查询的(使用类型转换器实现)
    • 返回为实体类 (使用类型转换器实现)
  • 使用原生sql 进行查询,插入,更新,删除
    • 返回出来的是 map 结构(需要单独处理)
    • 返回为实体类 (使用类型转换器实现)
    • 使用加密字段进行查询的(AOP + 注解实现

实现

类型转换器

实现AttributeConverter

@Slf4j
public class CryptoConvert implements AttributeConverter<String,String> {

    /**
     * 存入数据库前进行的转化操作
     * @param attribute
     * @return
     */
    @Override
    public String convertToDatabaseColumn(String attribute) {
        String encode = CryptoUtil.encode(attribute);
        log.info("oldValue:{},newValue:{}",attribute,encode);
        return encode;
    }

    /**
     * 取出数据库后进行的转化操作
     * @param dbData
     * @return
     */
    @Override
    public String convertToEntityAttribute(String dbData) {
        String decode = CryptoUtil.decode(dbData);
        log.info("oldValue:{},newValue:{}",dbData,decode);
        return decode;
    }
}
实体类修改
@Column(name="remark")
// 增加类型转换器,标注该字段需要使用类型转换器处理
@Convert(converter = CryptoConvert.class)
private java.lang.String remarks;

Crypto 注解

@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Crypto {
}

AOP

尝试使用@Before但是不能修改参数的值,所以这里使用@Around

@Slf4j
@Component
@Aspect
public class CryptoAspect {

    /**
     * 针对带有@Crypto注解的参数进行加密
     * @param joinPoint
     * @return
     * @throws Throwable
     */
    @Around(value = "execution(* *(..,@Crypto (String),..))")
    public Object encrypt(ProceedingJoinPoint joinPoint) throws Throwable {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Annotation[][] parameterAnnotations = signature.getMethod().getParameterAnnotations();
        Object[] oldArgs = joinPoint.getArgs();
        Object[] newArgs = Arrays.copyOf(oldArgs, oldArgs.length);

        for (int i = 0; i < newArgs.length; i++) {
            
            if (Objects.isNull(newArgs[i])) {
                continue;
            }
            if(!(newArgs[i] instanceof String)){
                continue;
            }
            Annotation[] parameterAnnotation = parameterAnnotations[i];
            if (ArrayUtils.isEmpty(parameterAnnotation)) {
                continue;
            }
            for (Annotation annotation : parameterAnnotation) {
                if (Crypto.class.equals(annotation.annotationType())) {
                    String str = (String) newArgs[i];
                    String encode = CryptoUtil.encode(str);
                    newArgs[i] = encode;
                }
            }
        }
        log.info("oldArgs:{} newArgs:{}",oldArgs,newArgs);
        // 使用解密后的参数调用对应方法
        return joinPoint.proceed(newArgs);
    }
}

目前可使用场景

// 1.JPA 使用方法名操作数据库                  remarks 为需要加密查询的字段 
List<Model> findByRemarks(@Param(value = "remarks") String remarks);

// 2.JPA 使用 @Query 注解并使用 HQL 操作数据库   remarks 为需要加密查询的字段 
@Query("select C FROM Model C where C.remarks = ?1")
List<Model> findByRemarks2(@Param(value = "remarks") String remarks);

// 3.JPA 使用 @Query 注解并使用 原生SQL 操作数据库   remarks 为需要加密查询的字段  (hibernate 查出数据库时也会使用类型转换器所以不再单独解密)
@Query(value = "select * from t_model where remark = ?1",nativeQuery = true)
List<Model> findByRemarks3(@Crypto @Param(value = "remarks") String remarks);

// 4.使用 Query + HQL   及可实现(hibernate 查出数据库时也会使用类型转换器所以不再单独解密)
@Override
public List<Model> getRemarks2(String remarks) {
    Query query = em.createQuery("select C FROM Model C where C.remarks = ?1",AccountBindHistory.class);
    query.setParameter(1, remarks);
    List<Model> resultList = query.getResultList();
    return resultList;
}

// 5.使用 Query + SQL  这个需要在入口处添加@Crypto 及可实现(hibernate 查出数据库时也会使用类型转换器所以不再单独解密)
@Override
public List<Model> getRemarks(@Crypto String remark) {
    Query query = em.createNativeQuery("select * from t_model where remark = ?",AccountBindHistory.class);
    query.setParameter(1, remark);
    List<Model> resultList = query.getResultList();
    return resultList;
}

// em.createNativeQuery 在创建Query的时候需要指定操作的映射类型

// 6.Spring-data-rest 生成的接口也支持自动加解密

注意

  1. 加密方式需要使用**对称加密,**也就是可以将明文转换出来的算法。
  2. 数据库中已有的数据需要进行处理,也就是需要加密。这样可以保证查询是与后入库的数据都能正常解密,

http://www.kler.cn/a/441436.html

相关文章:

  • STM32 FreeRTOS中断管理
  • 自动化01
  • [Unity 热更方案] 使用Addressable进行打包管理, 使用AssetBundle进行包的加载管理.70%跟练
  • composer安装指定php版本, 忽略平台原因导致的报错
  • 【JAVA 基础 第(20)课】JDBC JAVA 连接 MySql 数据库
  • ubuntu黑屏问题解决
  • webpack打包流程及原理
  • LeetCode 283.移动零(超简单讲解)
  • 鸿蒙权限请求工具类
  • 力扣-图论-15【算法学习day.65】
  • 【PyTorch】实现在训练过程中自定义动态调整学习率
  • 测试工程师八股文04|计算机网络 和 其他
  • 【日常笔记】基本数据类型浅析 -int类型能存储哪些传感器数据
  • 减少 Flutter 应用体积的常用方法
  • 在线PDF合并工具 - 快速、免费、安全的文档处理解决方案 | Online PDF Merger Tool
  • 力扣--LCR 164.破解闯关密码
  • K8s 中Istio 的使用示例
  • ThinkPHP 5.1 的模板布局功能
  • CentOS7源码编译安装nginx+php+mysql
  • 前端单元测试实战:从零开始构建可靠的测试体系
  • vue2项目中如何把rem设置为固定的100px
  • Linux:进程通信、管道通信
  • MFC CMDIChildWnd
  • 【Linux】socket编程1
  • jmeter后端监视器
  • selenium 在已打开浏览器上继续调试