系统数据加密传输的实现

news2024/9/24 17:09:27

文章目录

  • 1、背景
  • 2、需求
  • 3、实现思路
    • 3.1 密码加密
    • 3.2 密码解密
    • 3.3 nacos密码加密
  • 4、相关工具类
    • 4.1 非对称加密RSA
    • 4.2 对称加密AES
    • 4.3 Nacos加解密的实现:Jasypt
  • 5、历史数据兼容处理

1、背景

用户在浏览器发送请求数据到后台系统,期间数据在网络传输,如果这些数据是敏感数据,被恶意拦截时,就有安全问题,造成用户密码泄漏等等。

在这里插入图片描述
如此,可考虑使用非对称加密或者对称加密,给前端一个公钥,让前端把数据用公钥加密后传到后端,后端负责解密得到原始数据后再处理请求,如此,即使被恶意拦截,也无法得到真实密码。

对称加密即:文件的加密和解密都是使用相同的密钥。加密方和解密方使用同一把钥匙。

在这里插入图片描述

对称加密的优点是加密速度快,缺点是相对不安全(如果别人知道你用的哪种加密算法且密钥泄漏,则一切形同虚设)。

非对称加密即:两个密钥,公钥用来加密,私钥用来解密

在这里插入图片描述

和对称加密相比,安全性更高,但加解密更慢,数据量小时可采用。对称加密和非对称加密这两种思想,市面上有多种不同的具体落地的算法。对称加密如AES,非对称加密如RSA。 具体选择:

  • 文件很大建议使用对称加密
  • 文件较小,要求安全性高,建议采用非对称加密

2、需求

对系统中敏感数据进行加密,保证数据安全。敏感数据包括:用户密码、用户手机号、用户邮箱、Nacos配置中各个中间件的密码。加密方向包括:

  • 数据加密传输:前端调用后端接口时,先用公钥对密码进行加密,再使用base64编码,然后传输
  • 数据加密存储:后端落库时,base64解码,再用私钥解密,对明文密码要经过Bcrypt等不可逆加密算法加密后保存,防止被拖库

在这里插入图片描述

3、实现思路

3.1 密码加密

这里使用非对称加密实现更合理。针对以上要加密的数据,用户密码处理的流程图如下:修改注册后端接口,用户注册时,提交密码,前端用公钥对密码进行加密后,传到后端服务器。后端接口中用私钥对密码进行解密,实现加密传输。解密后,再对解密后的明文密码进行加密,存入数据库,实现加密存储。项目中用到了Spring Security框架,所以这里用Bcrypt算法进行加密存储,Bcrypt也可防止彩虹表破解。

在这里插入图片描述

对邮箱名、手机号等信息,可非对称加密,也可使用AES对称加密,实现加密传输,加密落库则可有可无,如果选择了加密落库,可能会影响到之前的userList接口等等,总之明文、密文别转换叉了。

3.2 密码解密

修改后端登录接口,登录时,前端传来的密码,解密后传到SpringSecurity框架,如果账户是加密传输的,也需解密,因为框架里要loadUserByUsername,用户名得转换过来。

在这里插入图片描述

流程图:

在这里插入图片描述

3.3 nacos密码加密

项目中,用Nacos做配置管理,很多中间件,如MySQL、Redis的密码都明文存储在配置文件中,考虑改为密文存储。SpringBoot服务启动时,去Nacos拉取配置、注册服务信息。改为密文后,需要先解密,才能连接中间件成功,实现这个可以考虑加一个Filter过滤器或者AOP,在读配置文件时,判断如果是密文,则解密后重新赋值。这里直接用已有的开源实现:Jasypt

//官方文档:
https://github.com/ulisesbocchio/jasypt-spring-boot
//源码解析:
https://blog.csdn.net/u013905744/article/details/86508236

大致看了下,实现思路是借助SpringBoot Bean加载的扩展点,做一个过滤器,如果读到的内容是以Jasypt指定的前后缀ENC(),则解密后重新赋值:

在这里插入图片描述

4、相关工具类

4.1 非对称加密RSA

加密和解密的方法:

import org.apache.tomcat.util.codec.binary.Base64;

import javax.crypto.Cipher;
import java.security.KeyFactory;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;

/**
 * 使用Cipher类实现RSA加密解密
 **/
public class RSAUtil {

    /**
     * 私钥
     */
    private static final String privateKey = "";
    /**
     * 公钥
     */
    private static final String publicKey = "";
    /**
     * 编码字符集
     */
    public static final String CHARSET = "UTF-8";
    /**
     * 算法定义
     */
    public static final String RSA_ALGORITHM = "RSA";

    /**
     * RSA公钥加密
     *
     * @param str 加密字符串
     * @return 返回加密字符串的base64值
     */
    public static String encrypt(String str) throws Exception {
        //base64编码的公钥
        byte[] decoded = Base64.decodeBase64(publicKey);
        RSAPublicKey pubKey = (RSAPublicKey) KeyFactory.getInstance(RSA_ALGORITHM).generatePublic(new X509EncodedKeySpec(decoded));
        //RSA加密并base64编码
        Cipher cipher = Cipher.getInstance(RSA_ALGORITHM);
        cipher.init(Cipher.ENCRYPT_MODE, pubKey);
        return Base64.encodeBase64String(cipher.doFinal(str.getBytes(CHARSET)));
    }

    /**
     * RSA私钥解密
     *
     * @param str 加密字符串
     * @return 返回解密后的明文
     * @throws Exception 解密过程中的异常信息
     */
    public static String decrypt(String str) throws Exception {
        //64位解码加密后的字符串
        byte[] inputByte = Base64.decodeBase64(str.getBytes(CHARSET));
        //base64编码的私钥
        byte[] decoded = Base64.decodeBase64(privateKey);
        RSAPrivateKey priKey = (RSAPrivateKey) KeyFactory.getInstance(RSA_ALGORITHM).generatePrivate(new PKCS8EncodedKeySpec(decoded));
        //RSA解密
        Cipher cipher = Cipher.getInstance(RSA_ALGORITHM);
        cipher.init(Cipher.DECRYPT_MODE, priKey);
        return new String(cipher.doFinal(inputByte));
    }

}


前端RSA加密:

// 安装jsencrypt
// npm i jsencrypt -S
 
import JSEncrypt from 'jsencrypt/bin/jsencrypt.min'
 
//公钥
const publicKey = ''
//私钥
const privateKey = ''
  
// 加密
export function encrypt(txt) {
  const encryptor = new JSEncrypt()
  encryptor.setPublicKey(publicKey) // 设置公钥
  return encryptor.encrypt(txt) // 对数据
}
  
// 解密(暂无使用)
export function decrypt(txt) {
  const encryptor = new JSEncrypt()
  encryptor.setPrivateKey(privateKey) // 设置私钥
  return encryptor.decrypt(txt) // 对数据进行解密
}

4.2 对称加密AES

加密和解密的方法:

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.util.Base64Utils;

import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;

/**
 * AES加密工具类
 */
@Slf4j
public class AESUtil {

    /**
     * 编码
     */
    private static final String ENCODING = "UTF-8";
    /**
     * 算法定义
     */
    private static final String AES_ALGORITHM = "AES";
    /**
     * 指定填充方式
     */
    private static final String CIPHER_PADDING = "AES/ECB/PKCS5Padding";

    /**
     * 密码
     */
    private static final String AES_KEY = "your-private-key-xx";


    /**
     * AES加密
     *
     * @param content 待加密内容
     * @return 加密后内容的base64值
     */
    public static String encrypt(String content) {
        if (StringUtils.isBlank(content)) {
            return content;
        }
        try {
            //对密码进行编码
            byte[] bytes = AES_KEY.getBytes(ENCODING);
            //设置加密算法,生成秘钥
            SecretKeySpec secretKeySpec = new SecretKeySpec(bytes, AES_ALGORITHM);
            // 算法/模式/补码方式
            Cipher cipher = Cipher.getInstance(CIPHER_PADDING);
            //选择加密
            cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec);
            //处理待加密内容生成字节数组
            byte[] encrypted = cipher.doFinal(content.getBytes(ENCODING));
            //返回base64字符串
            return Base64Utils.encodeToString(encrypted);
        } catch (Exception e) {
            log.error("AESUtil.encrypt content:{}, 加密异常", content, e);
            return content;
        }
    }

    /**
     * 解密
     *
     * @param content 待解密内容
     * @return 解密后的明文
     */
    public static String decrypt(String content) {
        if (StringUtils.isBlank(content)) {
            return content;
        }
        try {
            //对密码进行编码
            byte[] bytes = AES_KEY.getBytes(ENCODING);
            //设置解密算法,生成秘钥
            SecretKeySpec secretKeySpec = new SecretKeySpec(bytes, AES_ALGORITHM);
            // 算法/模式/补码方式
            Cipher cipher = Cipher.getInstance(CIPHER_PADDING);
            //选择解密
            cipher.init(Cipher.DECRYPT_MODE, secretKeySpec);

            //先进行Base64解码
            byte[] decodeBase64 = Base64Utils.decodeFromString(content);

            //根据待解密内容进行解密
            byte[] decrypted = cipher.doFinal(decodeBase64);

            //将字节数组转成字符串
            return new String(decrypted, ENCODING);
        } catch (Exception e) {
            log.error("AESUtil.decrypt content:{}, 解密异常", content, e);
            return content;
        }
    }

}

注意,不管是RSA的公钥私钥,还是AES的密钥,都重新生成了一次,以防止用户选择相对简单的密码,而被攻击者破解或推断密钥

在这里插入图片描述

4.3 Nacos加解密的实现:Jasypt

Jasypt 其实是一个专门用于加解密的库,用的是对称加密AES。jasypt-spring-boot-starter用在SpringBoot项目中的步骤:

  • 引入依赖
<dependency>
  <groupId>com.github.ulisesbocchio</groupId>
  <artifactId>jasypt-spring-boot-starter</artifactId>
  <version>3.0.5</version>
</dependency>

  • 增加密钥配置
jasypt:
  encryptor:
    password: hello!!!
    # 默认的加密算法是 PBEWITHHMACSHA512ANDAES_256,JDK9才支持,JDK1.8用不了,改为下面这个
    algorithm: PBEWithMD5AndDES

这个password就别放nacos了,否则password泄漏,其余密文照样不安全,可放在项目jar包里的配置文件,或者直接不写在配置文件,只是让运维在java -jar是指定一下这个password值

  • 引入Jasypt的加密类,改造Nacos中的明文
@Autowired
private StringEncryptor encryptor;

//明文变带有jasypt能识别前缀的密文
public String encrypt(String str) {
  return "ENC(" + encryptor.encrypt(str) + ")";
}

// 生成结果如:ENC(GT2vTn1+SdeFu90xH/vgw3uYTNyV5PGp),替换Nacos中对应的明文
  • 前面提到,Jasypt自己会识别是否为自己的密文,然后解密后重新赋值,所以改造后取值,依旧像之前一样直接取即可
@Value("${spring.redis.password}")
private String password;

5、历史数据兼容处理

对旧的明文存储的数据,需要处理为密文,系统有定时任务管理页面的话,可考虑加个定时任务,给运维人员去执行一次。如果没有,可考虑提供一个内部接口,调用一次,处理旧数据。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1915134.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

osgverse浏览器端编译

目录 1 WSL安装(Windows subsystem for Linux)2 emsdk准备3 SetUp.sh安装(osgverse源码目录下)4 显示与问题 内容 WSL安装(Windows subsystem for Linux) 安装wsl&#xff1a;wsl --install 将版本设置为wsl1(因为版本2比版本1慢很多)&#xff1a;wsl --set-version ubuntu 1…

防火墙基础实验配置

一&#xff0c;实验拓扑 二&#xff0c;实验需求&#xff1a; 1.DMZ区内的服务器&#xff0c;办公区仅能在办公时间内&#xff08;9&#xff1a;00 - 18&#xff1a;00&#xff09;可以访问&#xff0c;生产区的设备全天可以访问 2.生产区不允许访问互联网&#xff0c;办公区…

迂回战术:“另类“全新安装 macOS 15 Sequoia beta2 的极简方法

概述 随着 WWDC 24 的胜利闭幕&#xff0c;Apple 平台上各种 beta 版的系统也都“跃跃欲出”&#xff0c;在 mac 上自然也不例外。 本次全新的 macOS 15 Sequoia&#xff08;红杉&#xff09;包含了诸多重磅升级&#xff0c;作为秃头开发者的我们怎么能不先睹为快呢&#xff1…

什么是边缘计算?创造一个更快、更智慧、更互联的世界

前言 如今&#xff0c;数十亿物联网传感器广泛部署在零售商店、城市街道、仓库和医院等各种场所&#xff0c;正在生成大量数据。从这些数据中更快地获得洞察&#xff0c;意味着可以改善服务、简化运营&#xff0c;甚至挽救生命。但要做到这一点&#xff0c;企业需要实时做出决策…

网络协议 — Keepalived 高可用方案

目录 文章目录 目录Keepalived 是实现了 VRRP 协议的软件Keepalived 的软件架构VRRP StackCheckersKeepalived 的配置Global configurationvrrp_scriptVRRP Configurationvrrp synchroization groupvrrp instancevirtual ip addressesvirtual routesLVS Configurationvirtual_s…

Day06-角色管理-员工管理

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 1.编辑角色-进入行内编辑2.角色管理-行内编辑-数据缓存3.角色管理-编辑角色-确定取消4.角色管理-删除角色员工管理-页面结构6.员工管理-左侧树7.员工管理-选中首个节…

昇思学习打卡-14-ResNet50迁移学习

文章目录 数据集可视化预训练模型的使用部分实现 推理 迁移学习&#xff1a;在一个很大的数据集上训练得到一个预训练模型&#xff0c;然后使用该模型来初始化网络的权重参数或作为固定特征提取器应用于特定的任务中。本章学习使用的是前面学过的ResNet50&#xff0c;使用迁移学…

JAVA之开发神器——IntelliJ IDEA的下载与安装

一、IDEA是什么&#xff1f; IEAD是JetBrains公司开发的专用于java开发的一款集成开发环境。由于其功能强大且符合人体工程学&#xff08;就是更懂你&#xff09;的优点&#xff0c;深受java开发人员的喜爱。目前在java开发工具中占比3/4。如果你要走java开发方向&#xff0c;那…

985研究生8年终毕业,学位证颁发11天后被作废?

“正常是学校颁证给学院&#xff0c;但学院就没告诉我&#xff0c;还把学校颁发的证书给撤销了&#xff0c;这中间学院并没有书面或电话告知我本人。”34岁读研&#xff0c;如今已42岁的内蒙古任女士回想起求学不易&#xff0c;很是心酸。 2015年3月&#xff0c;任女士考取2015…

Python酷库之旅-第三方库Pandas(014)

目录 一、用法精讲 34、pandas.DataFrame.to_parquet函数 34-1、语法 34-2、参数 34-3、功能 34-4、返回值 34-5、说明 34-6、用法 34-6-1、数据准备 34-6-2、代码示例 34-6-3、结果输出 35、pandas.read_sql_table函数 35-1、语法 35-2、参数 35-3、功能 35-4…

【Neo4j】实战 (数据库技术丛书)学习笔记

Neo4j实战 (数据库技术丛书) 第1章演示了应用Neo4j作为图形数据库对改进性能和扩展性的可能性, 也讨论了对图形建模的数据如何正好适应于Neo4j数据模型,现在到了该动 手实践的时间了。第一章 概述 Neo4j将数据作为顶点和边存储(或者用Neo4j术语,节点和关系存 储)。用户被定…

C++初学者指南-5.标准库(第一部分)--顺序容器

C初学者指南-5.标准库(第一部分)–顺序容器 文章目录 C初学者指南-5.标准库(第一部分)--顺序容器标准顺序容器常见特点规律性&#xff1a;复制&#xff0c;分配&#xff0c;比较类型推导(C17)常用接口部分 array<T,size>vector\<T>C 的默认容器快速回顾迭代器范围插…

微调Qwen2大语言模型加入领域知识

这里写自定义目录标题 试用Qwen2做推理安装LLaMA-Factory使用自有数据集微调Qwen2验证微调效果 试用Qwen2做推理 参考&#xff1a;https://qwen.readthedocs.io/en/latest/getting_started/quickstart.html from transformers import AutoModelForCausalLM, AutoTokenizer de…

短视频矩阵系统多账号搭建技术源码(saas开发者技术独立搭建)

在构建云服务环境以部署虚拟机方面&#xff0c;以Amazon Web Services&#xff08;AWS&#xff09;为示例&#xff0c;需采购并配置适当数量的EC2实例以及相关网络设施。 接下来&#xff0c;根据业务需求&#xff0c;应创建多个社交媒体平台如抖音和快手的官方账户&#xff0c;…

便宜SSL证书有哪些平台推荐 域名SSL证书作用

在数字化时代&#xff0c;网络安全已成为我们日常生活和工作中不可或缺的一部分。 申请便宜SSL证书步骤 1、登录来此加密网站&#xff0c;输入域名&#xff0c;可以勾选泛域名和包含根域。 2、选择加密方式&#xff0c;一般选择默认就可以了&#xff0c;也可以自定义CSR。 3…

css 自定义变量 var()

现在新版本的UI框架&#xff0c;基本使用CSS变量 css的一个函数&#xff1a;var()&#xff0c;此函数在有些场景下能优化不少代码量。 var() 介绍 借用下W3C的定义&#xff1a; var() 函数用于插入自定义的属性值&#xff0c;如果一个属性值在多处被使用&#xff0c;该方法就…

MySQL--函数、约束、多表查询

函数 函数指一段可以直接被另一段程序调用的程序或代码 字符串函数、数值函数、日期函数、流程函数 字符串函数 数值函数 日期函数 datediff&#xff08;date1,date2&#xff09;&#xff1a;date1-date2 流程函数 约束 概念&#xff1a;约束是作用于表中字段上的规则&…

半导体硅太阳能电池基板的湿化学处理及电子界面特性

硅(Si)在半导体器件制造中的大多数技术应用都是基于这种材料的特定界面性能。二氧化硅&#xff08;二氧化硅&#xff09;可以通过简单的氧化方法在硅表面制备&#xff0c;其特点是高化学和电稳定性。晶体硅在光伏应用占主导地位&#xff0c;全球近90%的太阳能电池生产是基于多晶…

携手并进 共创未来丨东软睿驰与中国移动上海产业研究院达成战略合作

2024年7月10日&#xff0c;东软睿驰与中国移动上海产业研究院(以下简称“上研院”)在沈阳隆重举行战略合作签约仪式。东软睿驰董事长兼CEO王勇峰、高级副总裁邢志刚与上研院董事长王建中、副总经理黄刚等领导出席签约仪式。 图为东软睿驰与上研院战略合作签约仪式现场 东软睿驰…

新手小白的pytorch学习第一弹-------张量

1 导入pytorch包 import torch2 创建张量&#xff08;tensor&#xff09; scalar标量 scalar torch.tensor(7) scalartensor(7)scalar.ndim查看scalar的维度&#xff0c;因为scalar是标量&#xff0c;所以维度为0 0scalar.shapetorch.Size([])torch.item()7vector&#xf…