当前位置: 首页 > 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;则两幅图像等权重…...

Spring Boot 实现流式响应(兼容 2.7.x)

在实际开发中&#xff0c;我们可能会遇到一些流式数据处理的场景&#xff0c;比如接收来自上游接口的 Server-Sent Events&#xff08;SSE&#xff09; 或 流式 JSON 内容&#xff0c;并将其原样中转给前端页面或客户端。这种情况下&#xff0c;传统的 RestTemplate 缓存机制会…...

shell脚本--常见案例

1、自动备份文件或目录 2、批量重命名文件 3、查找并删除指定名称的文件&#xff1a; 4、批量删除文件 5、查找并替换文件内容 6、批量创建文件 7、创建文件夹并移动文件 8、在文件夹中查找文件...

FFmpeg 低延迟同屏方案

引言 在实时互动需求激增的当下&#xff0c;无论是在线教育中的师生同屏演示、远程办公的屏幕共享协作&#xff0c;还是游戏直播的画面实时传输&#xff0c;低延迟同屏已成为保障用户体验的核心指标。FFmpeg 作为一款功能强大的多媒体框架&#xff0c;凭借其灵活的编解码、数据…...

django filter 统计数量 按属性去重

在Django中&#xff0c;如果你想要根据某个属性对查询集进行去重并统计数量&#xff0c;你可以使用values()方法配合annotate()方法来实现。这里有两种常见的方法来完成这个需求&#xff1a; 方法1&#xff1a;使用annotate()和Count 假设你有一个模型Item&#xff0c;并且你想…...

【RockeMQ】第2节|RocketMQ快速实战以及核⼼概念详解(二)

升级Dledger高可用集群 一、主从架构的不足与Dledger的定位 主从架构缺陷 数据备份依赖Slave节点&#xff0c;但无自动故障转移能力&#xff0c;Master宕机后需人工切换&#xff0c;期间消息可能无法读取。Slave仅存储数据&#xff0c;无法主动升级为Master响应请求&#xff…...

多种风格导航菜单 HTML 实现(附源码)

下面我将为您展示 6 种不同风格的导航菜单实现&#xff0c;每种都包含完整 HTML、CSS 和 JavaScript 代码。 1. 简约水平导航栏 <!DOCTYPE html> <html lang"zh-CN"> <head><meta charset"UTF-8"><meta name"viewport&qu…...

Spring AI与Spring Modulith核心技术解析

Spring AI核心架构解析 Spring AI&#xff08;https://spring.io/projects/spring-ai&#xff09;作为Spring生态中的AI集成框架&#xff0c;其核心设计理念是通过模块化架构降低AI应用的开发复杂度。与Python生态中的LangChain/LlamaIndex等工具类似&#xff0c;但特别为多语…...

精益数据分析(97/126):邮件营销与用户参与度的关键指标优化指南

精益数据分析&#xff08;97/126&#xff09;&#xff1a;邮件营销与用户参与度的关键指标优化指南 在数字化营销时代&#xff0c;邮件列表效度、用户参与度和网站性能等指标往往决定着创业公司的增长成败。今天&#xff0c;我们将深入解析邮件打开率、网站可用性、页面参与时…...

【7色560页】职场可视化逻辑图高级数据分析PPT模版

7种色调职场工作汇报PPT&#xff0c;橙蓝、黑红、红蓝、蓝橙灰、浅蓝、浅绿、深蓝七种色调模版 【7色560页】职场可视化逻辑图高级数据分析PPT模版&#xff1a;职场可视化逻辑图分析PPT模版https://pan.quark.cn/s/78aeabbd92d1...

Docker 本地安装 mysql 数据库

Docker: Accelerated Container Application Development 下载对应操作系统版本的 docker &#xff1b;并安装。 基础操作不再赘述。 打开 macOS 终端&#xff0c;开始 docker 安装mysql之旅 第一步 docker search mysql 》〉docker search mysql NAME DE…...