当前位置: 首页 > 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 {

  }

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

相关文章:

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

为防止数据库泄露&#xff0c;表里的敏感字段被曝光&#xff0c;需要对用户的重要数据做加密存取。 选择加密算法&#xff1a; 首先&#xff0c;你需要选择适合你的需求的加密算法。一些常见的加密算法包括AES、RSA、SHA等。具体的选择取决于你要加密的数据和安全需求。 引入…...

计算机毕设 大数据工作岗位数据分析与可视化 - python flask

文章目录 0 前言1 课题背景2 实现效果3 项目实现3.1 概括 3.2 Flask实现3.3 HTML页面交互及Jinja2 4 **完整代码**5 最后 0 前言 &#x1f525; 这两年开始毕业设计和毕业答辩的要求和难度不断提升&#xff0c;传统的毕设题目缺少创新和亮点&#xff0c;往往达不到毕业答辩的要…...

Maven聚合项目配合Springcloud案例

创建maven项目 导入依赖 <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0"xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation"http://maven.apache…...

目标检测网络系列——YOLO V1

文章目录 One Stage DectectionYOLO网络正向预测pipline反向传播过程理解grid和grid对应的B个预测框YOLO网络的限制对比实验与其他的real-time detection的对比VOC 2007数据集的错误分析YOLO和Fast RCNN的集成学习VOC 2012数据集结果YOLO模型的泛化性DEMOOne Stage Dectection …...

任务工单发送失败重试方案设计

需求背景&#xff1a; 该系统为一个工单系统&#xff0c;其中任务工单为该系统中的一个模块&#xff1b;任务工单它是需要周期性调度的一种任务类型&#xff1b;可以按照用户配置的时间周期定时性触发的。由于任务需要发送到对应的工作人员上&#xff0c;所以这里需要先对员工进…...

关于 Vue-iClient-MapboxGL 的使用注意事项

官网&#xff1a;https://iclient.supermap.io/web/apis/vue/zh/api/guide/installation.html 关于图的使用&#xff0c;其余的引入步骤不再赘述&#xff0c;仅说注意事项。 推荐使用的是全局引入&#xff0c;也就是完整引入 因为单独引入我踩了不少坑&#xff0c;比如说 cs…...

Go 语言 map 如何顺序读取?

Go 语言中的 map 是一种非常强大的数据结构&#xff0c;它允许我们快速地存储和检索键值对。 然而&#xff0c;当我们遍历 map 时&#xff0c;会有一个有趣的现象&#xff0c;那就是输出的键值对顺序是不确定的。 现象 先看一段代码示例&#xff1a; package mainimport &q…...

flutter StreamSubscription 订阅者 stream

当您使用[Stream.listen]收听[Stream]时 则返回[StreamSubscription]对象 List<StreamSubscription?> subscriptions []; overridevoid initState() {super.initState();//subscriptions列表添加两个StreamSubscription。Stream.listen返回StreamSubscription对象subs…...

安全性算法

目录 一、安全性算法 二、基础术语 三、对称加密与非对称加密 四、数字签名 五、 哈希算法 六、哈希算法碰撞与溢出处理 一、安全性算法 安全性算法的必要性&#xff1a; 安全性算法的必要性是因为在现代数字化社会中&#xff0c;我们经常需要传输、存储和处理敏感的数据…...

解决ASP.NET Core的中间件无法读取Response.Body的问题

概要 本文主要介绍如何在ASP.NET Core的中间件中&#xff0c;读取Response.Body的方法&#xff0c;以便于我们实现更多的定制化开发。本文介绍的方法适用于.Net 3.1 和 .Net 6。 代码和实现 现象解释 首先我们尝试在自定义中间件中直接读取Response.Body&#xff0c;代码如…...

DownloadingImages 下载缓存图片,显示图片文字列表

1. 用到的技术点: 1) Codable : 可编/解码 JSON 数据 2) background threads : 后台线程 3) weak self : 弱引用 4) Combine : 取消器/组合操作 5) Publishers and Subscribers : 发布者与订阅者 6) FileManager : 文件管理器 7) NSCache : 缓存 2. 网址: 2.1 测试接口网址: …...

【应用层协议】HTTPS的加密流程

目录 一、认识HTTPS 二、密文 1、对称加密 2、非对称加密 三、HTTPS加密流程 1、建立连接 2、证书验证 3、密钥协商 4、数据传输 5、关闭连接 总结 在数字化时代&#xff0c;互联网已经成为我们生活和工作中不可或缺的一部分。然而&#xff0c;随着数据的不断增加&a…...

最新AI创作系统/AI绘画系统/ChatGPT系统+H5源码+微信公众号版+支持Prompt应用

一、AI创作系统 SparkAi创作系统是基于国外很火的ChatGPT进行开发的AI智能问答系统和AI绘画系统。本期针对源码系统整体测试下来非常完美&#xff0c;可以说SparkAi是目前国内一款的ChatGPT对接OpenAI软件系统。那么如何搭建部署AI创作ChatGPT&#xff1f;小编这里写一个详细图…...

Z410 2023款无人机,专为零基础开发者打造的入门级开源无人机

为什么开发Z410升级款-Easydrone无人机 新手开发者通常在本科阶段加入人工智能行业&#xff0c;对无人机二次开发往往一知半解&#xff0c;面临着C、Python、ROS和mavlink等一系列入门知识&#xff0c;学习起来非常困难&#xff0c;学习的过程中也面临许多挫折。为了帮助零基础…...

elementui修改message消息提示颜色

/* el弹出框样式 */ .el-message {top: 80px !important;border: 0; }.el-message * {color: var(--white) !important;font-weight: 600; }.el-message--success {background: var(--themeBackground); }.el-message--warning {background: var(--gradientBG); }.el-message--…...

Linux和Hadoop的学习

目录 1. Linux的常用快捷键2. Hadoop集群部署问题汇总 1. Linux的常用快捷键 复制&#xff1a;CtrlshiftC 粘贴&#xff1a;CtrlshiftV TAB&#xff1a;补全命令 编写输入&#xff1a;i 退出编写&#xff1a;esc 保存并退出&#xff1a;shift&#xff1a; 2. Hadoop集群部署问…...

通达信指标预警信号,自动发送给微信好友1.0

1.功能介绍&#xff1a;十一节假日期间写了一个&#xff0c;可将股票指标预警信号&#xff0c;自动发送给微信好友/微信群&#xff08;即电脑端的消息&#xff0c;通过模拟微信操作可在手机上显示&#xff09;。本工具按通达信写的&#xff0c;如果大智慧&#xff0c;同花顺也能…...

浅谈CDN内容分发与全局负载均衡

CDN简介 CDN的全称是Content Delivery Network&#xff0c;即内容分发网络。CDN是构建在现有网络基础之上的智能虚拟网络&#xff0c;依靠部署在各地的边缘服务器&#xff0c;通过中心平台的负载均衡、内容分发、调度等功能模块&#xff0c;使用户就近获取所需内容&#xff0c…...

【框架风格】解释器模式

1、描述 解释器框架风格&#xff08;Interpreter Framework Style&#xff09;是一种软件架构风格&#xff0c;其核心思想是构建一个解释器&#xff08;Interpreter&#xff09;来解释并执行特定领域或问题领域的语言或规则。以下是解释器框架风格的一些特点&#xff1a; 1. 领…...

c++视觉图像线性混合

图像线性混合 使用 cv::addWeighted() 函数对两幅图像进行线性混合。alpha 和 beta 是两幅图像的权重&#xff0c;它们之和应该等于1。gamma 是一个可选的增益&#xff0c;这里设置为0。 你可以通过调整 alpha 的值来改变混合比例。如果 alpha0.5&#xff0c;则两幅图像等权重…...

大话软工笔记—需求分析概述

需求分析&#xff0c;就是要对需求调研收集到的资料信息逐个地进行拆分、研究&#xff0c;从大量的不确定“需求”中确定出哪些需求最终要转换为确定的“功能需求”。 需求分析的作用非常重要&#xff0c;后续设计的依据主要来自于需求分析的成果&#xff0c;包括: 项目的目的…...

ardupilot 开发环境eclipse 中import 缺少C++

目录 文章目录 目录摘要1.修复过程摘要 本节主要解决ardupilot 开发环境eclipse 中import 缺少C++,无法导入ardupilot代码,会引起查看不方便的问题。如下图所示 1.修复过程 0.安装ubuntu 软件中自带的eclipse 1.打开eclipse—Help—install new software 2.在 Work with中…...

MySQL 8.0 OCP 英文题库解析(十三)

Oracle 为庆祝 MySQL 30 周年&#xff0c;截止到 2025.07.31 之前。所有人均可以免费考取原价245美元的MySQL OCP 认证。 从今天开始&#xff0c;将英文题库免费公布出来&#xff0c;并进行解析&#xff0c;帮助大家在一个月之内轻松通过OCP认证。 本期公布试题111~120 试题1…...

数据库分批入库

今天在工作中&#xff0c;遇到一个问题&#xff0c;就是分批查询的时候&#xff0c;由于批次过大导致出现了一些问题&#xff0c;一下是问题描述和解决方案&#xff1a; 示例&#xff1a; // 假设已有数据列表 dataList 和 PreparedStatement pstmt int batchSize 1000; // …...

网络编程(UDP编程)

思维导图 UDP基础编程&#xff08;单播&#xff09; 1.流程图 服务器&#xff1a;短信的接收方 创建套接字 (socket)-----------------------------------------》有手机指定网络信息-----------------------------------------------》有号码绑定套接字 (bind)--------------…...

Springboot社区养老保险系统小程序

一、前言 随着我国经济迅速发展&#xff0c;人们对手机的需求越来越大&#xff0c;各种手机软件也都在被广泛应用&#xff0c;但是对于手机进行数据信息管理&#xff0c;对于手机的各种软件也是备受用户的喜爱&#xff0c;社区养老保险系统小程序被用户普遍使用&#xff0c;为方…...

Linux nano命令的基本使用

参考资料 GNU nanoを使いこなすnano基础 目录 一. 简介二. 文件打开2.1 普通方式打开文件2.2 只读方式打开文件 三. 文件查看3.1 打开文件时&#xff0c;显示行号3.2 翻页查看 四. 文件编辑4.1 Ctrl K 复制 和 Ctrl U 粘贴4.2 Alt/Esc U 撤回 五. 文件保存与退出5.1 Ctrl …...

C# winform教程(二)----checkbox

一、作用 提供一个用户选择或者不选的状态&#xff0c;这是一个可以多选的控件。 二、属性 其实功能大差不差&#xff0c;除了特殊的几个外&#xff0c;与button基本相同&#xff0c;所有说几个独有的 checkbox属性 名称内容含义appearance控件外观可以变成按钮形状checkali…...

轻量级Docker管理工具Docker Switchboard

简介 什么是 Docker Switchboard &#xff1f; Docker Switchboard 是一个轻量级的 Web 应用程序&#xff0c;用于管理 Docker 容器。它提供了一个干净、用户友好的界面来启动、停止和监控主机上运行的容器&#xff0c;使其成为本地开发、家庭实验室或小型服务器设置的理想选择…...

goreplay

1.github地址 https://github.com/buger/goreplay 2.简单介绍 GoReplay 是一个开源的网络监控工具&#xff0c;可以记录用户的实时流量并将其用于镜像、负载测试、监控和详细分析。 3.出现背景 随着应用程序的增长&#xff0c;测试它所需的工作量也会呈指数级增长。GoRepl…...