[Spring Boot]登录密码三种加密方式
简述
介绍其三种密码加密方法
1.SM2加密与验签
2.随机密码盐加密
3.MD5加密
推荐使用方法1,其次使用方法2,最不推荐的是方法3。方法3极其容易被密码字典破解,如果项目进行安全测试,通常是不允许的加密方式。
SM2加密与验签
引入bcprov,以使用SM2加密。
<dependency><groupId>org.bouncycastle</groupId><artifactId>bcprov-jdk15to18</artifactId><version>1.69</version></dependency>
工具类与测试方法
加密的主要工具类如下,其中带有测试的main方法。
import lombok.extern.slf4j.Slf4j;
import org.bouncycastle.asn1.gm.GMObjectIdentifiers;
import org.bouncycastle.crypto.InvalidCipherTextException;
import org.bouncycastle.crypto.engines.SM2Engine;
import org.bouncycastle.crypto.params.ECDomainParameters;
import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
import org.bouncycastle.crypto.params.ECPublicKeyParameters;
import org.bouncycastle.crypto.params.ParametersWithRandom;
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey;
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.jce.spec.ECParameterSpec;import java.security.*;
import java.security.spec.ECGenParameterSpec;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
import java.util.Map;/*** sm2算法与签名的使用** @author fir* @date 2024/7/23 14:22*/
@Slf4j
public class Sm2SignatureUtils {static {Security.addProvider(new BouncyCastleProvider());}public static final String PUBLIC_KEY = "publicKey";public static final String PRIVATE_KEY = "privateKey";/*** 生成国密公私钥对*/public static Map<String, String> generateSmKey() throws Exception {KeyPairGenerator keyPairGenerator;SecureRandom secureRandom = new SecureRandom();ECGenParameterSpec sm2Spec = new ECGenParameterSpec("sm2p256v1");keyPairGenerator = KeyPairGenerator.getInstance("EC", new BouncyCastleProvider());keyPairGenerator.initialize(sm2Spec);keyPairGenerator.initialize(sm2Spec, secureRandom);KeyPair keyPair = keyPairGenerator.generateKeyPair();PrivateKey privateKey = keyPair.getPrivate();PublicKey publicKey = keyPair.getPublic();String publicKeyStr = new String(Base64.getEncoder().encode(publicKey.getEncoded()));String privateKeyStr = new String(Base64.getEncoder().encode(privateKey.getEncoded()));return Map.of(PUBLIC_KEY, publicKeyStr, PRIVATE_KEY, privateKeyStr);}/*** 将Base64转码的公钥串,转化为公钥对象*/public static PublicKey createPublicKey(String publicKey) {PublicKey publickey = null;try {X509EncodedKeySpec publicKeySpec = new X509EncodedKeySpec(Base64.getDecoder().decode(publicKey));KeyFactory keyFactory = KeyFactory.getInstance("EC", new BouncyCastleProvider());publickey = keyFactory.generatePublic(publicKeySpec);} catch (Exception e) {log.error("将Base64转码的公钥串,转化为公钥对象异常:{}", e.getMessage(), e);}return publickey;}/*** 将Base64转码的私钥串,转化为私钥对象*/public static PrivateKey createPrivateKey(String privateKey) {PrivateKey publickey = null;try {PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(Base64.getDecoder().decode(privateKey));KeyFactory keyFactory = KeyFactory.getInstance("EC", new BouncyCastleProvider());publickey = keyFactory.generatePrivate(pkcs8EncodedKeySpec);} catch (Exception e) {log.error("将Base64转码的私钥串,转化为私钥对象异常:{}", e.getMessage(), e);}return publickey;}/*** 根据publicKey对原始数据data,使用SM2加密*/public static String encrypt(byte[] data, String publicKeyBase64) {PublicKey publicKey = createPublicKey(publicKeyBase64);ECPublicKeyParameters localEcPublicKeyParameters = getEcPublicKeyParameters(publicKey);SM2Engine localSm2Engine = new SM2Engine();localSm2Engine.init(true, new ParametersWithRandom(localEcPublicKeyParameters, new SecureRandom()));byte[] arrayOfByte2;try {arrayOfByte2 = localSm2Engine.processBlock(data, 0, data.length);return Base64.getEncoder().encodeToString(arrayOfByte2);} catch (InvalidCipherTextException e) {log.error("SM2加密失败:{}", e.getMessage(), e);return null;}}private static ECPublicKeyParameters getEcPublicKeyParameters(PublicKey publicKey) {ECPublicKeyParameters localEcPublicKeyParameters = null;if (publicKey instanceof BCECPublicKey localEcPublicKey) {ECParameterSpec localEcParameterSpec = localEcPublicKey.getParameters();ECDomainParameters localEcDomainParameters = new ECDomainParameters(localEcParameterSpec.getCurve(),localEcParameterSpec.getG(), localEcParameterSpec.getN());localEcPublicKeyParameters = new ECPublicKeyParameters(localEcPublicKey.getQ(), localEcDomainParameters);}return localEcPublicKeyParameters;}/*** 根据privateKey对加密数据encode data,使用SM2解密*/public static String decrypt(String encodeBase64, String privateKeyBase64) {SM2Engine localSm2Engine = new SM2Engine();PrivateKey privateKey = createPrivateKey(privateKeyBase64);BCECPrivateKey sm2PriK = (BCECPrivateKey) privateKey;byte[] encodeData = Base64.getDecoder().decode(encodeBase64);ECParameterSpec localEcParameterSpec = sm2PriK.getParameters();ECDomainParameters localEcDomainParameters = new ECDomainParameters(localEcParameterSpec.getCurve(),localEcParameterSpec.getG(), localEcParameterSpec.getN());ECPrivateKeyParameters localEcPrivateKeyParameters = new ECPrivateKeyParameters(sm2PriK.getD(),localEcDomainParameters);localSm2Engine.init(false, localEcPrivateKeyParameters);try {byte[] result = localSm2Engine.processBlock(encodeData, 0, encodeData.length);return new String(result);} catch (InvalidCipherTextException e) {log.error("SM2解密失败:{}", e.getMessage(), e);return null;}}/*** 私钥,数据,生成签名*/public static String signByPrivateKey(String dataStr, String privateKeyBase64) throws Exception {PrivateKey privateKey = createPrivateKey(privateKeyBase64);byte[] data = Base64.getDecoder().decode(dataStr);Signature sig = Signature.getInstance(GMObjectIdentifiers.sm2sign_with_sm3.toString(), BouncyCastleProvider.PROVIDER_NAME);sig.initSign(privateKey);sig.update(data);byte[] sign = sig.sign();return Base64.getEncoder().encodeToString(sign);}/*** 公钥与签名验证数据合法性*/public static boolean verifyByPublicKey(String dataStr, String publicKeyBase64, String signatureBase64) throws Exception {PublicKey publicKey = createPublicKey(publicKeyBase64);byte[] signature = Base64.getDecoder().decode(signatureBase64);byte[] data = Base64.getDecoder().decode(dataStr);Signature sig = Signature.getInstance(GMObjectIdentifiers.sm2sign_with_sm3.toString(), BouncyCastleProvider.PROVIDER_NAME);sig.initVerify(publicKey);sig.update(data);return sig.verify(signature);}public static void test() throws Exception {// 生成公私钥对Map<String, String> keys = generateSmKey();String publicKey = keys.get(PUBLIC_KEY);String privateKey = keys.get(PRIVATE_KEY);String testStr = "123456";System.out.println("原始字符串:" + testStr);System.out.println("公钥:" + keys.get(PUBLIC_KEY));System.out.println("私钥:" + keys.get(PRIVATE_KEY));System.out.println();// 公钥加密String encrypt = encrypt(testStr.getBytes(), publicKey);System.out.println("加密数据:" + encrypt);// 私钥签名,后续根据数据与公钥验签String sign = signByPrivateKey(encrypt, privateKey);System.out.println("数据签名:" + sign);// 公钥验签,验证通过后再进行数据解密boolean b = verifyByPublicKey(encrypt, publicKey, sign);System.out.println("数据验签:" + b);//私钥解密String decrypt = decrypt(encrypt, privateKey);System.out.println("解密数据:" + decrypt);}public static void uesCase() throws Exception {//生成公私钥对String publicKey = "MFkwEwYHKoZIzj0CAQYIKoEcz1UBgi0DQgAEsrdE0XrAO2S7Ize0tm0r3diH9cPH23t0J9yVDtiVux6g71msH5YGTWW6/ogQSCVJ4iaofgCS/ly5+wkXa+/IGg==";String privateKey = "MIGTAgEAMBMGByqGSM49AgEGCCqBHM9VAYItBHkwdwIBAQQgNxb+Jcu1vhGt9UEbeFUYeCC+RWL7+sfUL1vnhBp2KtKgCgYIKoEcz1UBgi2hRANCAASyt0TResA7ZLsjN7S2bSvd2If1w8fbe3Qn3JUO2JW7HqDvWawflgZNZbr+iBBIJUniJqh+AJL+XLn7CRdr78ga";String testStr = "123456";System.out.println("原始字符串:" + testStr);System.out.println("公钥:" + publicKey);System.out.println("私钥:" + privateKey);System.out.println();//公钥加密String encrypt = encrypt(testStr.getBytes(), publicKey);System.out.println("加密数据:" + encrypt);// 私钥签名,后续根据数据与公钥验签String sign = signByPrivateKey(encrypt, privateKey);System.out.println("数据签名:" + sign);//公钥验签,验证通过后再进行数据解密boolean b = verifyByPublicKey(encrypt, publicKey, sign);System.out.println("数据验签:" + b);//私钥解密String decrypt = decrypt(encrypt, privateKey);System.out.println("解密数据:" + decrypt);}public static void main(String[] args) {try {test();
// uesCase();} catch (Exception e) {throw new RuntimeException(e);}}
}
使用案例
验证密码
接受到用户输入的用户与密码之后,在数据库中查询出旧的密码,并进行旧密码进行验签、解密。解密后判断用户输入的密码与数据库存储的密码是否相同。
// 验证密码是否正确String password = "123456";String passwordOld = user.getPassword();String signature = user.getSignature();String decryptPasswordOld = null;try {// 公钥验签,查看数据与签名是否有效boolean b = Sm2SignatureUtils.verifyByPublicKey(passwordOld, public, signature);if(!b){throw new CommonException("数据损坏");}decryptPasswordOld = Sm2SignatureUtils.decrypt(passwordOld, private);}catch (Exception e){throw new CommonException("数据损坏");}if (decryptPasswordOld == null || !decryptPasswordOld.equals(password)) {throw new CommonException("用户密码错误");}
修改密码
接收到用户的密码后,根据公私要生成加密数据串与私钥签名。并存储到数据库,用于之后的密码验证。
// 公钥加密String password = "123456";String encrypt = Sm2SignatureUtils.encrypt(password.getBytes(), public);// 根据数据与私钥,生成私钥签名String sign = Sm2SignatureUtils.signByPrivateKey(encrypt, private);user.setPassword(encrypt);user.setSignature(sign);
随机密码盐加密
工具类与测试方法
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Base64;/*** @author fir* @date 2024/7/25 12:22*/
public class SaltUtils {/*** 生成随机安全盐** @return 盐*/public static String generateSalt() {// 使用SecureRandom生成安全的随机盐SecureRandom random = new SecureRandom();byte[] salt = new byte[16];random.nextBytes(salt);return Base64.getEncoder().encodeToString(salt);}/*** 生成加盐密码** @return 盐*/public static String hashPassword(String password, String salt){// 将密码和盐结合String saltedPassword = password + salt;// 使用SHA-256进行哈希MessageDigest md;try {md = MessageDigest.getInstance("SHA-256");}catch (NoSuchAlgorithmException e){throw new RuntimeException("加密盐处理失败");}byte[] hashedBytes = md.digest(saltedPassword.getBytes());// 将哈希值转换为字符串return Base64.getEncoder().encodeToString(hashedBytes);}public static void main(String[] args){String salt = generateSalt();String password = "123456";String hashPassword = hashPassword(password, salt);System.out.println("盐:" + salt);System.out.println("密码:" + password);System.out.println("加密密码:" + hashPassword);}}
使用案例
验证密码
查询出用户的密码,将用户输入的密码盐加密,并判断与数据库的加密密码是否一致。
String password = "123456";String salt = user.getSalt();String passwordOld = user.getPassword();String mPassword = SaltUtils.hashPassword(password, salt);if (!mPassword.equals(passwordOld)) {throw new CommonException("密码错误");}
修改密码
将用户输入的密码md5加密之后,存在数据库中,用于之后的密码验证
String password = "123456";String salt = SaltUtils.generateSalt();String hashPassword = SaltUtils.hashPassword(password, salt);user.setPassword(hashPassword);
MD5加密
引入hutool包,以使用md5加密。
<dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId><version>5.8.19</version></dependency>
测试方法
import cn.hutool.crypto.digest.MD5;/*** @author fir* @date 2024/7/22 10:07*/
public class Md5Utils {public static void main(String[] args){// MD5取值String mPassword = MD5.create().digestHex("123456");System.out.println(mPassword);}
}
使用案例
验证密码
查询出用户的密码,将用户输入的密码MD5加密,并判断与数据库的加密密码是否一致。
String password = "123456";String mPassword = MD5.create().digestHex(password);String passwordOld = user.getPassword();if (!mPassword.equals(passwordOld)) {throw new CommonException("密码错误");}
修改密码
将用户输入的密码md5加密之后,存在数据库中,用于之后的密码验证
String password = "123456";String passwordMd5 = MD5.create().digestHex(password);user.setPassword(passwordMd5);
相关文章:
[Spring Boot]登录密码三种加密方式
简述 介绍其三种密码加密方法 1.SM2加密与验签 2.随机密码盐加密 3.MD5加密 推荐使用方法1,其次使用方法2,最不推荐的是方法3。方法3极其容易被密码字典破解,如果项目进行安全测试,通常是不允许的加密方式。 SM2加密与验签 引入…...
前端面试项目细节重难点分享(十三)
面试题提问:分享你最近做的这个项目,并讲讲该项目的重难点? 答:最近这个项目是一个二次迭代开发项目,迭代周期一年,在做这些任务需求时,确实有很多值得分享的印象深刻的点,我讲讲下面…...
每天五分钟深度学习:向量化方式完成逻辑回归m个样本的前向传播
本文重点 我们已经知道了向量化可以明显的加速程序的运行速度,本节课程将使用向量化来完成逻辑回归的前向传播,不使用一个for循环。 逻辑回归的前向传播 我们先来回忆一下逻辑回归的前向传播,如果我们有m个训练样本,首先对第一个样本进行预测,我们需要计算z,然后计算预…...
以线程完成并发的UDP服务端
网络(九)并发的UDP服务端 以线程完成功能 客户端 // todo UDP发送端 #include <stdio.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <sys/types.h> #include <stdlib.h> #include <string.h…...
linux c 特殊字符分割
/* * brief: 根据split_symbol分割字符串 * param: str为要分割的字符串,split_symbol是分隔符 * return:返回garray的指针数组,如果返回非空需要自己处理释放 */ GPtrArray_autoptr char_sz_spilt(pchar* str, pchar split_symbol) {if (NUL…...
搭建本地私有知识问答系统:MaxKB + Ollama + Llama3 (wsl网络代理配置、MaxKB-API访问配置)
目录 搭建本地私有知识问答系统:MaxKB、Ollama 和 Llama3 实现指南引言MaxKB+Ollama+Llama 3 Start buildingMaxKB 简介:1.1、docker部署 MaxKB(方法一)1.1.1、启用wls或是开启Hyper使用 WSL 2 的优势1.1.2、安装docker1.1.3、docker部署 MaxKB (Max Knowledge Base)MaxKB …...
谷粒商城实战笔记-65-商品服务-API-品牌管理-表单校验自定义校验器
文章目录 1,el-form品牌logo图片自定义显示2,重新导入和注册element-ui组件3,修改brand-add-or-update.vue控件的表单校验规则firstLetter 校验规则sort 校验规则 1,el-form品牌logo图片自定义显示 为了在品牌列表中自定义显示品…...
学好C++之——命名空间
c开始学习之时,你不可避免会遇到一个新朋友,那就是——namespace(命名空间)。 那么这篇文章就来为你解决这个小麻烦喽~ 目录 1.namespace存在的意义 2.namespace的定义 3.namespace的使用 1.namespace存在的意义 在C中&#…...
pytorch lightning报错all tensors to be on the same device
RuntimeError: Expected all tensors to be on the same device, but found at least two devices, cuda:0 and cpu! 修改指定为gpu trainer pl.Trainer(max_epochstrain_params.iterations, loggertb_logger,acceleratorgpu, devices1)...
Redis中的哨兵(Sentinel)
上篇文章我们讲述了Redis中的主从复制(Redis分布式系统中的主从复制-CSDN博客),本篇文章针对主从复制中的问题引出Redis中的哨兵,希望本篇文章会对你有所帮助。 文章目录 一、引入哨兵机制 二、基本概念 三、主从复制的问题 四、哨…...
产业创新研究杂志产业创新研究杂志社产业创新研究编辑部2024年第12期目录
高质量发展 如何在新一轮产业链变革中平稳应对挑战 王宏利; 1-3《产业创新研究》投稿:cnqikantg126.com 基于ERGM的城市间绿色低碳技术专利转让网络结构及演化研究 吕彦朋;姜军;张宁; 4-6 数字基础设施建设对城市FDI的影响——基于“宽带中国”试点政策…...
网闸(Network Gatekeeper或Security Gateway)
本心、输入输出、结果 文章目录 网闸(Network Gatekeeper或Security Gateway)前言网闸主要功能网闸工作原理网闸使用场景网闸网闸(Network Gatekeeper或Security Gateway) 编辑 | 简简单单 Online zuozuo 地址 | https://blog.csdn.net/qq_15071263 如果觉得本文对你有帮助…...
C#中的字符串
String 在实例方法中string虽然传入的是引用类型 但是修改string 并不是修改原来堆里面的值 而是又重新创建一个堆值 用来然后用方法内的变量指向新的堆值 C# 中的字符串(string 类型)提供了许多有用的方法来处理字符串数据。以下是一些常用的字符…...
docker安装部署elasticsearch7.15.2
docker安装部署elasticsearch7.15.2 1.拉取es镜像 docker pull docker.elastic.co/elasticsearch/elasticsearch:7.15.2如果不想下载或者镜像拉去太慢可以直接下载文章上面的镜像压缩包 使用镜像解压命令 docker load -i elasticsearch-7-15-2.tar如下图所示就表示镜像解压成…...
Symfony 入门指南:快速安装与基础配置
Symfony 入门指南:快速安装与基础配置 Symfony 是一个强大而灵活的 PHP 框架,广泛应用于构建现代 Web 应用程序。本指南将带您一步一步地了解如何快速安装 Symfony,并完成基本配置,以便您能够开始使用这个强大的框架。 目录 引…...
3.3V升压至5V的AH6922芯片:高效能的SOP8封装解决方案
# 3.3V升压至5V的AH6922芯片:高效能的SOP8封装解决方案 在当今快速发展的电子设备领域,对于电源管理的需求日益增长。特别是对于便携式产品和手持设备,一个高效、稳定且体积小巧的升压解决方案变得至关重要。本文将介绍一款专为这些需求设计…...
赋能未来教育,3DCAT助力深圳鹏程技师学院打造5G+XR实训室
随着国家对教育行业的重视,实训室建设已成为推动教育现代化的关键。《教育信息化2.0行动计划》、《职业教育示范性虚拟仿真实训基地建设指南》等政策文件,明确指出了加强虚拟仿真实训教学环境建设的重要性。 在这一大背景下,教育行业对于实训…...
力扣141环形链表问题|快慢指针算法详细推理,判断链表是否有环|龟兔赛跑算法
做题链接 目录 前言: 一、算法推导: 1.假设有环并且一定会相遇,那么一定是在环内相遇,且是快指针追上慢指针。 2.有环就一定会相遇吗?快指针是每次跳两步,有没有可能把慢指针跳过去? 3.那一定…...
React 常见的报错及解决方法
1、Warning: Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons(无效的钩子调用。钩子只能在函数组件的内部调用。这可能是由于以下原因之一) 原因&#x…...
更新服务器nginx 1.26.1版本
今天在官网下载了nginx1的1.26.1版本,使用gpt的脚本想直接覆盖安装,脚本如下 #!/bin/bash# 设置变量 NGINX_VERSION"1.26.1" TAR_FILE"nginx-$NGINX_VERSION.tar.gz" SRC_DIR"nginx-$NGINX_VERSION"# 检查是否存在tar包 …...
华为云AI开发平台ModelArts
华为云ModelArts:重塑AI开发流程的“智能引擎”与“创新加速器”! 在人工智能浪潮席卷全球的2025年,企业拥抱AI的意愿空前高涨,但技术门槛高、流程复杂、资源投入巨大的现实,却让许多创新构想止步于实验室。数据科学家…...
[2025CVPR]DeepVideo-R1:基于难度感知回归GRPO的视频强化微调框架详解
突破视频大语言模型推理瓶颈,在多个视频基准上实现SOTA性能 一、核心问题与创新亮点 1.1 GRPO在视频任务中的两大挑战 安全措施依赖问题 GRPO使用min和clip函数限制策略更新幅度,导致: 梯度抑制:当新旧策略差异过大时梯度消失收敛困难:策略无法充分优化# 传统GRPO的梯…...
基于距离变化能量开销动态调整的WSN低功耗拓扑控制开销算法matlab仿真
目录 1.程序功能描述 2.测试软件版本以及运行结果展示 3.核心程序 4.算法仿真参数 5.算法理论概述 6.参考文献 7.完整程序 1.程序功能描述 通过动态调整节点通信的能量开销,平衡网络负载,延长WSN生命周期。具体通过建立基于距离的能量消耗模型&am…...
【Redis技术进阶之路】「原理分析系列开篇」分析客户端和服务端网络诵信交互实现(服务端执行命令请求的过程 - 初始化服务器)
服务端执行命令请求的过程 【专栏简介】【技术大纲】【专栏目标】【目标人群】1. Redis爱好者与社区成员2. 后端开发和系统架构师3. 计算机专业的本科生及研究生 初始化服务器1. 初始化服务器状态结构初始化RedisServer变量 2. 加载相关系统配置和用户配置参数定制化配置参数案…...
定时器任务——若依源码分析
分析util包下面的工具类schedule utils: ScheduleUtils 是若依中用于与 Quartz 框架交互的工具类,封装了定时任务的 创建、更新、暂停、删除等核心逻辑。 createScheduleJob createScheduleJob 用于将任务注册到 Quartz,先构建任务的 JobD…...
Java多线程实现之Callable接口深度解析
Java多线程实现之Callable接口深度解析 一、Callable接口概述1.1 接口定义1.2 与Runnable接口的对比1.3 Future接口与FutureTask类 二、Callable接口的基本使用方法2.1 传统方式实现Callable接口2.2 使用Lambda表达式简化Callable实现2.3 使用FutureTask类执行Callable任务 三、…...
ffmpeg(四):滤镜命令
FFmpeg 的滤镜命令是用于音视频处理中的强大工具,可以完成剪裁、缩放、加水印、调色、合成、旋转、模糊、叠加字幕等复杂的操作。其核心语法格式一般如下: ffmpeg -i input.mp4 -vf "滤镜参数" output.mp4或者带音频滤镜: ffmpeg…...
[Java恶补day16] 238.除自身以外数组的乘积
给你一个整数数组 nums,返回 数组 answer ,其中 answer[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积 。 题目数据 保证 数组 nums之中任意元素的全部前缀元素和后缀的乘积都在 32 位 整数范围内。 请 不要使用除法,且在 O(n) 时间复杂度…...
Unity | AmplifyShaderEditor插件基础(第七集:平面波动shader)
目录 一、👋🏻前言 二、😈sinx波动的基本原理 三、😈波动起来 1.sinx节点介绍 2.vertexPosition 3.集成Vector3 a.节点Append b.连起来 4.波动起来 a.波动的原理 b.时间节点 c.sinx的处理 四、🌊波动优化…...
处理vxe-table 表尾数据是单独一个接口,表格tableData数据更新后,需要点击两下,表尾才是正确的
修改bug思路: 分别把 tabledata 和 表尾相关数据 console.log() 发现 更新数据先后顺序不对 settimeout延迟查询表格接口 ——测试可行 升级↑:async await 等接口返回后再开始下一个接口查询 ________________________________________________________…...
