Springboot AOP对指定敏感字段数据加密存储的实现

 更新时间:2021年11月19日 11:55:53   作者:小目标青年  
本篇文章主要介绍了利用Springboot+AOP对指定的敏感数据进行加密存储以及对数据中加密的数据的解密的方法,代码详细,具有一定的价值,感兴趣的小伙伴可以了解一下

前言

本文主要内容:

1. 插入数据 自定义注解方式  对 指定接口方法 的 参数的指定字段进行 加密存储;

2.对数据内的加密数据,进行解密返回

先看看效果 :

数据存入数据库表内, 手机号phone和邮箱email 属于敏感数据,我们需要密文存储 :

查询解密返回:

1.  自定义注解 加密标识注解  NeedEncrypt.java :

import java.lang.annotation.*;
 
/**
 * @Author JCccc
 * @Description 需加密
 * @Date 2021/7/23 11:55
 */
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface NeedEncrypt {
 
 
}

2.自定义注解 需加密字段标识注解 EncryptField.java :

/**
 * @Author JCccc
 * @Description
 * @Date 2021/7/23 11:55
 */
@Target({ElementType.FIELD,ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
public @interface EncryptField {
 
    String[] value() default "";
}

3.加密逻辑的aop处理器  EncryptAspect.java :

import com.elegant.dotest.aop.annotation.EncryptField;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.jasypt.encryption.StringEncryptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
 
import java.lang.reflect.Field;
import java.util.Objects;
 
/**
 * @Author JCccc
 * @Description
 * @Date 2021/9/14 8:55
 */
@Slf4j
@Aspect
@Component
public class EncryptAspect {
 
    @Autowired
    private StringEncryptor stringEncryptor;
 
    @Pointcut("@annotation(com.elegant.dotest.aop.annotation.NeedEncrypt)")
    public void pointCut() {
    }
 
    @Around("pointCut()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        //加密
        encrypt(joinPoint);
 
        return joinPoint.proceed();
    }
 
    public void encrypt(ProceedingJoinPoint joinPoint)  {
        Object[] objects=null;
        try {
             objects = joinPoint.getArgs();
            if (objects.length != 0) {
                for (int i = 0; i < objects.length; i++) {
                    //抛砖引玉 ,可自行扩展其他类型字段的判断
                    if (objects[i] instanceof String) {
                        objects[i] = encryptValue(objects[i]);
                    } else {
                        encryptObject(objects[i]);
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
 
    /**
     * 加密对象
     * @param obj
     * @throws IllegalAccessException
     */
    private void encryptObject(Object obj) throws IllegalAccessException {
 
        if (Objects.isNull(obj)) {
            log.info("当前需要加密的object为null");
            return;
        }
        Field[] fields = obj.getClass().getDeclaredFields();
        for (Field field : fields) {
            boolean containEncryptField = field.isAnnotationPresent(EncryptField.class);
            if (containEncryptField) {
                //获取访问权
                field.setAccessible(true);
                String value = stringEncryptor.encrypt(String.valueOf(field.get(obj)));
                field.set(obj, value);
            }
        }
    }
 
    /**
     * 加密单个值
     * @param realValue
     * @return
     */
    public String encryptValue(Object realValue) {
        try {
            realValue = stringEncryptor.encrypt(String.valueOf(realValue));
        } catch (Exception e) {
            log.info("加密异常={}",e.getMessage());
        }
        return String.valueOf(realValue);
    }
 
 
}

4. 插入user表 使用的 User.java :

import com.elegant.dotest.aop.annotation.EncryptField;
import lombok.Data;
import lombok.experimental.Accessors;
 
/**
 * @Author JCccc
 * @Description
 * @Date 2021/9/14 8:55
 */
@Data
@Accessors(chain = true)
public class User {
 
    private Integer id;
    private String name;
    @EncryptField
    private String phone;
    @EncryptField
    private String email;
    private Integer age;
 
}

可以看到,手机号phone 和 邮箱 email 两个字段,我们做了注解 @EncryptField 标识:

ok,我们写个测试接口,使用 @NeedEncrypt 注解标识这个接口需要进行加密拦截 :

使用postman调用一下测试接口:

可以看下数据库,数据已经加密存储成功:

接下来是查询解密环节:

解密这里其实有些小讲究。 因为查询出来的数据有可能是单个实体,也可能是List (其实甚至是Map或者Set,又或者是 分页数据类)

所以本文将会以 最常用的 单个实体 、 List<实体> 为例子,去做解密。

1.解密自定义注解 NeedDecrypt.java :

/**
 * @Author JCccc
 * @Description 需解密
 * @Date 2021/7/23 11:55
 */
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface NeedDecrypt {
 
}

2. 解密逻辑的aop处理器  DecryptAspect.java : 

import com.elegant.dotest.aop.annotation.EncryptField;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.jasypt.encryption.StringEncryptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
 
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
 
/**
 * @Author JCccc
 * @Description
 * @Date 2021/9/14 8:55
 */
@Slf4j
@Aspect
@Component
public class DecryptAspect {
 
    @Autowired
    private StringEncryptor stringEncryptor;
 
    @Pointcut("@annotation(com.elegant.dotest.aop.annotation.NeedDecrypt)")
    public void pointCut() {
    }
 
    @Around("pointCut()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        //解密
        Object result = decrypt(joinPoint);
        return result;
    }
 
    public Object decrypt(ProceedingJoinPoint joinPoint) {
        Object result = null;
        try {
            Object obj = joinPoint.proceed();
            if (obj != null) {
                //抛砖引玉 ,可自行扩展其他类型字段的判断
                if (obj instanceof String) {
                    decryptValue(obj);
                } else {
                    result = decryptData(obj);
                }
            }
        } catch (Throwable e) {
            e.printStackTrace();
        }
        return result;
    }
 
    private Object decryptData(Object obj) throws IllegalAccessException {
 
        if (Objects.isNull(obj)) {
            return null;
        }
        if (obj instanceof ArrayList) {
            decryptList(obj);
        } else {
            decryptObj(obj);
        }
 
 
        return obj;
    }
 
    /**
     * 针对单个实体类进行 解密
     * @param obj
     * @throws IllegalAccessException
     */
    private void decryptObj(Object obj) throws IllegalAccessException {
        Field[] fields = obj.getClass().getDeclaredFields();
        for (Field field : fields) {
            boolean hasSecureField = field.isAnnotationPresent(EncryptField.class);
            if (hasSecureField) {
                field.setAccessible(true);
                String realValue = (String) field.get(obj);
                String value = stringEncryptor.decrypt(realValue);
                field.set(obj, value);
            }
        }
    }
 
    /**
     * 针对list<实体来> 进行反射、解密
     * @param obj
     * @throws IllegalAccessException
     */
    private void decryptList(Object obj) throws IllegalAccessException {
        List<Object> result = new ArrayList<>();
        if (obj instanceof ArrayList) {
            for (Object o : (List<?>) obj) {
                result.add(o);
            }
        }
        for (Object object : result) {
            decryptObj(object);
        }
    }
 
 
    public String decryptValue(Object realValue) {
        try {
            realValue = stringEncryptor.encrypt(String.valueOf(realValue));
        } catch (Exception e) {
            log.info("解密异常={}", e.getMessage());
        }
        return String.valueOf(realValue);
    }
 
 
}

然后我们对一个查询方法进行测试 :


我们先试一下查询单条数据的:

使用@NeedDecrypt注解标记这个接口需要数据解密:

调用接口看看结果:

然后是多条数据List<User> 返回的接口:

调用接口测试看看结果:

到此这篇关于Springboot AOP对指定敏感字段数据加密存储的实现的文章就介绍到这了,更多相关数据加密存储内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 手把手教你如何在Idea中下载jar包

    手把手教你如何在Idea中下载jar包

    maven依赖的jar包,很多时候同一个jar包会存在多个版本,删除其中一个后,重新编译,会把旧jar由加载回来了,下面这篇文章主要给大家介绍了关于如何在Idea中下载jar包的相关资料,需要的朋友可以参考下
    2023-06-06
  • 从Springboot项目中下载文件的具体过程

    从Springboot项目中下载文件的具体过程

    最近在做一个临时的项目,APP端在检测到程序有更新时,需要去后台下载新的安装包,接下来通过本文给大家分享从Springboot项目中下载文件的具体过程,感兴趣的朋友一起看看吧
    2021-07-07
  • Java8 HashMap的实现原理分析

    Java8 HashMap的实现原理分析

    Java8之后新增挺多新东西,接下来通过本文给大家介绍Java8 HashMap的实现原理分析,对java8 hashmap实现原理相关知识感兴趣的朋友一起学习吧
    2016-03-03
  • 编辑器Ueditor和SpringBoot 的整合方法

    编辑器Ueditor和SpringBoot 的整合方法

    本文通过实例代码给大家介绍了编辑器Ueditor和SpringBoot 的整合方法,需要的朋友参考下吧
    2017-08-08
  • java数据结构和算法中哈希表知识点详解

    java数据结构和算法中哈希表知识点详解

    在本篇文章里小编给大家分享了关于java数据结构和算法中哈希表的相关知识点内容,需要的朋友们学习下。
    2019-06-06
  • java volatile关键字作用及使用场景详解

    java volatile关键字作用及使用场景详解

    在本文里我们给大家分享的是关于java volatile关键字作用及使用场景的相关知识点内容,需要的朋友们学习下。
    2019-08-08
  • 基于Java实现马踏棋盘游戏算法

    基于Java实现马踏棋盘游戏算法

    这篇文章主要为大家详细介绍了基于Java实现马踏棋盘游戏算法,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-02-02
  • Springmvc拦截器执行顺序及各方法作用详解

    Springmvc拦截器执行顺序及各方法作用详解

    这篇文章主要介绍了Springmvc的拦截器执行顺序及各方法作用,下面讲实现其接口的写法,先看一下这个接口的三个方法,需要的朋友可以参考下
    2018-07-07
  • Mabitis中的#与$符号区别及用法介绍

    Mabitis中的#与$符号区别及用法介绍

    这篇文章主要介绍了Mabitis中的#与$符号区别,需要的朋友可以参考下
    2017-02-02
  • 实例讲解spring boot 多线程

    实例讲解spring boot 多线程

    这篇文章主要介绍了spring boot 多线程的相关资料,文中示例代码非常详细,帮助大家更好的理解和学习,感兴趣的朋友可以了解下
    2020-07-07

最新评论