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

Springboot 框架中加解密字段后存储数据库

为防止数据库泄露,表里的敏感字段被曝光,需要对用户的重要数据做加密存取。

选择加密算法: 首先,你需要选择适合你的需求的加密算法。一些常见的加密算法包括AES、RSA、SHA等。具体的选择取决于你要加密的数据和安全需求。

引入加密库: 在你的Spring Boot项目中,引入适用于你所选加密算法的库。例如,如果你选择使用AES加密,可以引入Java的javax.crypto库。

创建加密服务: 创建一个用于加密和解密的服务类。这个服务类应该包含加密和解密方法,并将其注入到Spring容器中以供使用。

定义数据库实体类: 在数据库实体类中,将需要加密的字段定义为加密前的原始字段。在将数据保存到数据库之前,使用加密服务对这些字段进行加密。

加密和解密数据: 在业务逻辑中,调用加密服务对需要加密的数据进行加密,并在需要时进行解密。确保加密密钥的安全存储,不要将密钥硬编码在代码中。

定义接口文件--------------------------------------------------

DecryptField.java :

package xxx.xxx

import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.ElementType;

//解密字段注解
@Target(value = {ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface DecryptField {
}

EncryptField .java :

//解密字段注解
@Target(value = {ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface EncryptField {
}

NeedDecrypt .java :

//作用于对返回值进行解密处理的方法上
@Target(value = {ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface NeedDecrypt {
}

NeedEncrypt .java :

//作用于对参数进行加密处理的方法上
@Target(value = {ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface NeedEncrypt {
}

AesSupport.java 加密解密算法------------------------------------------

public class AesSupport{
    
    private static final String KEY_ALGORITHM = "AES";
    private static final String DEFAULT_CIPHER_ALGORITHM = "AES/ECB/PKCS5Padding";
    private static final String AES_KEY = "you aes key"; // 16/24/32-character secret key

    private String password;
    private SecretKeySpec secretKeySpec;
    private static AesSupport instance;

    // 私有构造函数,防止外部创建实例
    private AesSupport() {
    }

    // 提供获取单例实例的方法,使用双重检查锁定保证线程安全
    public static AesSupport getInstance() throws NoSuchAlgorithmException {
        if (instance == null) {
            synchronized (AesSupport.class) {
                if (instance == null) {
                    instance = new AesSupport(AES_KEY);
                }
            }
        }
        return instance;
    }

    public AesSupport(String password) throws NoSuchAlgorithmException {

        if(StringUtils.isEmpty(password)){
            throw new IllegalArgumentException("password should not be null!");
        }

        this.password = password;
        this.secretKeySpec = getSecretKey(password);
    }

    public String encrypt(String value){

        try{
            Cipher cipher = Cipher.getInstance(DEFAULT_CIPHER_ALGORITHM);
            cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec);

            byte[] content = value.getBytes("UTF-8");
            byte[] encryptData = cipher.doFinal(content);

            return Hex.bytesToHexString(encryptData);
        }catch (Exception e){
            throw new IllegalStateException("AES encrypt "+e.getMessage(),e);
        }
    }

    public String decrypt(String value){
        if (StringUtils.isEmpty(value)) {
            return "";
        }
        try {
            byte[] encryptData = Hex.hexStringToBytes(value);
            Cipher cipher = Cipher.getInstance(DEFAULT_CIPHER_ALGORITHM);
            cipher.init(Cipher.DECRYPT_MODE, secretKeySpec);
            byte[] content = cipher.doFinal(encryptData);
            return new String(content, "UTF-8");
        }catch (Exception e){
            throw new IllegalStateException("AES decrypt "+e.getMessage(),e);
        }

    }


    private SecretKeySpec getSecretKey(final String password) throws NoSuchAlgorithmException{
        KeyGenerator kg = KeyGenerator.getInstance(KEY_ALGORITHM);
        SecureRandom random = SecureRandom.getInstance("SHA1PRNG");
        random.setSeed(password.getBytes());
        kg.init(128, random);
        SecretKey secretKey = kg.generateKey();
        return new SecretKeySpec(secretKey.getEncoded(), KEY_ALGORITHM);
    }
}
 

DecryptAop.java 解密Aop文件:

@Component
@Aspect
public class DecryptAop {
   
    /**
     * 定义需要解密的切入点
     */
    @Pointcut(value = "@annotation(xxx..service.crypt.NeedDecrypt)")
    public void pointcut() {
    }
 
    /**
     * 命中的切入点时的环绕通知
     *
     * @param proceedingJoinPoint
     * @return
     * @throws Throwable
     */
    @Around("pointcut()")
    public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        //执行目标方法
        Object result = proceedingJoinPoint.proceed();
        //判断目标方法的返回值类型
        if (result instanceof List) {
            for (Object tmp : ((List) result)) {
                //数据脱敏处理逻辑
                this.deepProcess(tmp);
            }
        } else {
            this.deepProcess(result);
        }
        //log.info("//环绕通知 end");
        return result;
    }
 
    public void deepProcess(Object obj) throws IllegalAccessException {
        if (obj != null) {
            //取出输出对象的所有字段属性,并遍历
            Field[] declaredFields = obj.getClass().getDeclaredFields();
            for (Field declaredField : declaredFields) {
                //判断字段属性上是否标记DecryptField注解
                if (declaredField.isAnnotationPresent(DecryptField.class)) {
                    //如果判断结果为真,则取出字段属性数据进行解密处理
                    declaredField.setAccessible(true);
                    Object valObj = declaredField.get(obj);
                    if (valObj != null) {
                        String value = valObj.toString();
                        //加密数据的解密处理
                        try {
                            value = AesSupport.getInstance().decrypt(value);
                        } catch (Exception e) {
                            // TODO Auto-generated catch block
                            e.printStackTrace();
                        }
                        DecryptField annotation = declaredField.getAnnotation(DecryptField.class);
                        //boolean open = annotation.open();
                        //把解密后的数据重新赋值
                        declaredField.set(obj, value);
                    }
                }
            }
        }
    }

}


EncryptAop.java 加密Aop文件:

@Component
@Aspect
public class EncryptAop {

    /**
     * 定义加密切入点
     */
    @Pointcut(value = "@annotation(xxx.service.crypt.NeedEncrypt)")
    public void pointcut() {
    }
 
    /**
     * 命中加密切入点的环绕通知
     *
     * @param proceedingJoinPoint
     * @return
     * @throws Throwable
     */
    @Around("pointcut()")
    public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {

        //获取命中目标方法的入参数
        Object[] args = proceedingJoinPoint.getArgs();
        if (args.length > 0) {
            for (Object arg : args) {
                //按参数的类型进行判断,如果业务中还有其他的类型,可酌情增加
                if (arg != null) {
                    if (arg instanceof List) {
                        for (Object tmp : ((List) arg)) {
                            //加密处理
                            this.deepProcess(tmp);
                        }
                    } else {
                        this.deepProcess(arg);
                    }
                }
            }
        }
        //对敏感数据加密后执行目标方法
        Object result = proceedingJoinPoint.proceed();
        return result;
    }
 
    public void deepProcess(Object obj) throws IllegalAccessException {
        if (obj != null) {
            //获取对象的所有字段属性并遍历
            Field[] declaredFields = obj.getClass().getDeclaredFields();
            for (Field declaredField : declaredFields) {
                //判断字段属性上是否标记了@EncryptField注解
                if (declaredField.isAnnotationPresent(EncryptField.class)) {
                    //如果判断结果为真,则取出字段属性值,进行加密、重新赋值
                    declaredField.setAccessible(true);
                    Object valObj = declaredField.get(obj);
                    if (valObj != null) {
                        String value = valObj.toString();
                        //开始敏感字段属性值加密
                        String decrypt;
                        try {
                            decrypt = AesSupport.getInstance().encrypt(value);
                             //把加密后的字段属性值重新赋值
                            declaredField.set(obj, decrypt);
                        } catch (Exception e) {
                            // TODO Auto-generated catch block
                            e.printStackTrace();
                        }
                    }
                }
            }
        }
    }

}

通过Aop切入拦截标注了NeedEncrypt 、NeedDecrypt的接口函数,获取到参数字段判断是否标注了DecryptField、EncryptField,对标注的进行加解密操作,最后存储到数据库。

在Bean对象类进行字段标注:

public class User{
    private String id;
    @EncryptField
    @DecryptField
    private String name;
    @EncryptField
    @DecryptField
    private String phonenum;

}

在接口方法上进行方法标注:

 @NeedEncrypt
  public int updateUser(User user) throws Exception {

  }

@NeedDecrypt
  public List<User> queryUser(String userId) throws Exception {

  }

注意不要在接口调用路径上进行重复标注,否则会加密两次,最后导致解密不出来。

http://www.lryc.cn/news/185243.html

相关文章:

  • 计算机毕设 大数据工作岗位数据分析与可视化 - python flask
  • Maven聚合项目配合Springcloud案例
  • 目标检测网络系列——YOLO V1
  • 任务工单发送失败重试方案设计
  • 关于 Vue-iClient-MapboxGL 的使用注意事项
  • Go 语言 map 如何顺序读取?
  • flutter StreamSubscription 订阅者 stream
  • 安全性算法
  • 解决ASP.NET Core的中间件无法读取Response.Body的问题
  • DownloadingImages 下载缓存图片,显示图片文字列表
  • 【应用层协议】HTTPS的加密流程
  • 最新AI创作系统/AI绘画系统/ChatGPT系统+H5源码+微信公众号版+支持Prompt应用
  • Z410 2023款无人机,专为零基础开发者打造的入门级开源无人机
  • elementui修改message消息提示颜色
  • Linux和Hadoop的学习
  • 通达信指标预警信号,自动发送给微信好友1.0
  • 浅谈CDN内容分发与全局负载均衡
  • 【框架风格】解释器模式
  • c++视觉图像线性混合
  • Doris 2.0.1 DockerFile版 升级实战
  • kotlin aes 加密解密
  • sqlite3的lib和头文件在哪下载 2023/9/19 上午10:46:43
  • 磁通量概述
  • MySql 终端常用指令
  • 【React-hooks篇幅】自定义hooks
  • 面试算法21:删除倒数第k个节点
  • 数据结构——排序算法(C语言)
  • 基于Http Basic Authentication的接口
  • 【yaml文件的编写】
  • kt6368A双模蓝牙芯片无法透传 可能是什么问题呢