前端加密的方式汇总

目录

一、Base64编码

二、哈希算法

三、对称加密(AES/DES)

四、非对称加密(RSA)

五、加盐

六、Web Cryptography API 

七、总结


随着信息和数据安全重要性的日益凸显,如何保证信息数据在传输的过程中的安全成为开发者重点关注的内容。前端加密通常是指在浏览器中对各种传输的数据进行各种加密操作。然而前端加密更多的是用来对传输的数据进行简单的混淆,为了确保数据在传输过程中不被轻易的篡改和读取。可供我们选择的加密方式有很多种,需要我们在开发过程中根据实际的场景选择适合自己的加密解决方案。那么,本文将结合应用场景来介绍一下前端开发中常用的加密方法。

一、Base64编码


严格意义上来说,Base64并不是一种加密方式,它是一种编码方式。Base64就是一种基于64个可打印字符来表示二进制数据的方法。Base64,就是包括小写字母a-z、大写字母A-Z、数字0-9、符号"+"、"/"一共64个字符的字符集,任何符号都可以转换成这个字符集中的字符,这个转换过程就叫做base64编码。

Base64编码是从二进制到字符的过程,可用于在HTTP环境下传递较长的标识信息。采用Base64编码具有不可读性,需要解码后才能阅读。Base64由于以上优点被广泛应用于计算机的各个领域。JavaScript中可以使用内置的btoa函数进行Base64编码。btoa函数可以将二进制数据转换为Base64编码的字符串,同样也可使用内置函数atob对编码后的字符串进行解码。

// 编码
const base64Str = window.btoa('Hello World!');
console.log(base64Str); // SGVsbG8gV29ybGQh
// 解码
const str = window.atob(base64Str);
console.log(str); // Hello World!

当然,我们也可以使用base64插件。

pnpm add js-base64

使用方法:

import { Base64 } from 'js-base64';// 编码const encode = Base64.encode('Hello World!'); console.log(encode);// 解码const decode = Base64.decode(encode); console.log(decode);

二、哈希算法


散列算法(Hash Algorithm),又称哈希算法,杂凑算法,是一种从任意文件中创造小的数字”指纹“的方法。与指纹一样,散列算法就是一种以较短的信息来保证文件唯一性的标志,这种标志与文件的每一个字节都相关,而且难以找到逆向规律。因此,当原有文件发生改变时,其标志值也会发生改变,从而告诉文件使用者当前的文件已经不是你所需求的文件。

Hash 算法能将将任意长度的二进制明文映射为较短的二进制串的算法,并且不同的明文很难映射为相同的 Hash 值。

哈希算法(Hash Algorithm)是一种将任意长度的消息映射为固定长度的消息摘要(Message Digest)的算法。哈希算法可以将任意长度的输入数据转换为固定长度的输出,通常称为哈希值(Hash Value)或摘要(Digest),并且满足以下特性:

(1) 确定性:对于相同的输入数据,哈希算法会生成相同的哈希值。

(2) 不可逆性:无法从哈希值中推导出原始的输入数据。

(3) 唯一性:不同的输入数据生成的哈希值应尽可能不同。

(4) 散列性:即使输入数据仅有微小的变化,生成的哈希值应该有很大的差异。

因此,哈希算法广泛应用于密码学、数据完整性校验、数字签名、数据分片等领域。

(1) 数字签名:将原始数据的哈希值与签名一起存储,以验证签名的完整性和正确性。

(2) 密码存储:将用户密码的哈希值存储在数据库中,以避免直接存储明文密码,提高安全性。

(3) 数据完整性校验:将原始数据的哈希值与传输过程中的哈希值进行比对,以判断数据是否被篡改。

(4) 数据分片:将原始数据分成若干个块,对每个块分别计算哈希值,以便快速检测数据块的正确性。

比较常见的哈希算法有MD5、SHA-1、SHA-256和SHA-512等。

安装crypto-js:

pnpm add crypto-js

使用方法:

import CryptoJS from "crypto-js";// MD5
const hash = CryptoJS.MD5('Message');
// SHA-1
const hash = CryptoJS.SHA1('Message');
// SHA-256
const hash = CryptoJS.SHA256('Message');
// SHA-512
const hash = CryptoJS.SHA512('Message');

三、对称加密(AES/DES)


DES全称为Data Encryption Standardy即数据加密标准,是一种使用密钥加密的算法。

AES全称为Advanced Encryption Standard,AES 算法使用分组密码体制,将明文按照固定大小进行分组,然后对每一分组进行加密。在加密过程中,AES 算法采用了多轮加密的方式,每一轮加密都包含了四种操作:SubBytes、ShiftRows、MixColumns 和 AddRoundKey。通过这些操作,AES 算法可以更加安全高效地对数据进行加密。

AES和DES加密算法是一种对称加密方式,所谓对称加密就是加密与解密使用的秘钥(一组字符串)是一个。加密和解密端必须使用同一个密钥的才可以进行加密和解密。

AES 加密最常用的模式就是 ECB模式和CBC模式,当然还有很多其它模式,它们都属于AES加密。ECB模式和CBC模式俩者区别就是 ECB 不需要 iv偏移量,而CBC需要。

ECB是一种基础的加密方式,密文被分割成分组长度相等的块(不足补齐),然后单独一个个加密,一个个输出组成密文。CBC是一种循环模式,前一个分组的密文和当前分组的明文异或或操作后再加密,这样做的目的是增强破解难度。

参数Mode为DES/AES的工作方式。

参数padding为填充模式,如果加密后密文长度如果达不到指定整数倍(8个字节、16个字节),填充对应字符,padding的赋值固定为CryptoJS.pad.Pkcs7即可。

import CryptoJS from "crypto-js";//秘钥
const secretKey = CryptoJS.enc.Utf8.parse("12345678ABCDEFGH"); //16位
// iv偏移量
const iv = CryptoJS.enc.Utf8.parse("ABCDEFGH12345678");
// 需要加密的参数,数据类型为bytes
const secretMessage = "Hello World!";
// 加密
const encrypt = CryptoJS.AES.encrypt(secretMessage, secretKey, {iv: iv,mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.Pkcs7 
}).tostring();
console.log(encrypt);//解密
const decrypt = CryptoJS.AES.decrypt(encrypt, secretKey, {iv: iv,mode: CryptoJS.mode.CBC,padding: CryptoJS.pad.Pkcs7
}).toString(CryptoJS.enc.Utf8);
console.log(decrypt);

四、非对称加密(RSA)


非对称加密,加密和解密的秘钥不是同一个秘钥,这里需要两把钥匙,一个公钥, 一个私钥, 公钥发送给客户端。发送端用公钥对数据进行加密,再发送给接收端,接收端使用私钥来对数据解密。由于私钥只存放在接受端这边,所以即使数据被截获了,也是无法进行解密的。

公钥与私钥是一对,因为加密和解密使用的是两个不同的密钥,所以这种算法叫作非对称加密算法。使用时都是使用公匙加密使用私匙解密。公匙可以公开,私匙自己保留。这个公钥和私钥其实就是一组数字,其二进制位长度可以是1024位或者2048位。长度越长其加密强度越大,相对就比较安全。所以目前为止,这种加密算法一直被广泛使用。

常见的非对称加密算法有RSA,DSA等等,本文我们就介绍一个RSA加密,也是最常见的一种加密方案。

jsencrypt就是一个基于RSA加解密的js库。

npm install jsencrypt 
import JSEncrypt from 'jsencrypt' 
// RSA加密
// 创建加密对象实例
const encryptor = new JSEncrypt(); 
//之前ssl生成的公钥,复制的时候要小心不要有空格
const pubKey = '-----BEGIN PUBLIC KEY-----MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC1QQRl0HlrVv6kGqhgonD6A9SU6ZJpnEN+Q0blT/ue6Ndt97WRfxtSAs0QoquTreaDtfC4RRX4o+CU6BTuHLUm+eSvxZS9TzbwoYZq7ObbQAZAY+SYDgAA5PHf1wNN20dGMFFgVS/y0ZWvv1UNa2laEz0I8Vmr5ZlzIn88GkmSiQIDAQAB-----END PUBLIC KEY-----';
//设置公钥
encryptor.setPublicKey(pubKey);
// 对内容进行加密
const encrypted = encryptor.encrypt('要加密的内容') // RSA解密
//创建解密对象实例
const decrypt = new JSEncrypt();
//之前ssl生成的秘钥
const priKey  = '-----BEGIN RSA PRIVATE KEY-----MIICXAIBAAKBgQC1QQRl0HlrVv6kGqhgonD6A9SU6ZJpnEN+Q0blT/ue6Ndt97WRfxtSAs0QoquTreaDtfC4RRX4o+CU6BTuHLUm+eSvxZS9TzbwoYZq7ObbQAZAY+SYDgAA5PHf1wNN20dGMFFgVS/y0ZWvv1UNa2laEz0I8Vmr5ZlzIn88GkmSiQIDAQABAoGBAKYDKP4AFlXkVlMEP5hS8FtuSrUhwgKNJ5xsDnFV8sc3yKlmKp1a6DETc7N66t/Wdb3JVPPSAy+7GaYJc7IsBRZgVqhrjiYiTO3ZvJv3nwAT5snCoZrDqlFzNhR8zvUiyAfGD1pExBKLZKNH826dpfoKD2fYlBVOjz6i6dTKBvCJAkEA/GtL6q1JgGhGLOUenFveqOHJKUydBAk/3jLZksQqIaVxoB+jRQNOZjeSO9er0fxgI2kh0NnfXEvH+v326WxjBwJBALfTRar040v71GJq1m8eFxADIiPDNh5JD2yb71FtYzH9J5/d8SUHI/CUFoROOhxr3DpagmrnTn28H0088vubKe8CQDKMOhOwx/tS5lqvN0YQj7I6JNKEaR0ZzRRuEmv1pIpAW1S5gTScyOJnVn1tXxcZ9xagQwlT2ArfkhiNKxjrf5kCQAwBSDN5+r4jnCMxRv/Kv0bUbY5YWVhw/QjixiZTNn81QTk3jWAVr0su4KmTUkg44xEMiCfjI0Ui3Ah3SocUAxECQAmHCjy8WPjhJN8y0MXSX05OyPTtysrdFzm1pwZNm/tWnhW7GvYQpvE/iAcNrNNb5k17fCImJLH5gbdvJJmCWRk=-----END RSA PRIVATE KEY----';
//设置秘钥
decrypt.setPrivateKey(priKey);
//解密之前拿公钥加密的内容
const uncrypted = decrypt.decrypt(encrypted)


RSA加解密可以应用在用户注册或登录的时候,用公钥对密码进行加密,再去传给后台,后台用私钥对加密的内容进行解密,然后进行密码校验或者保存到数据库。


五、加盐


加盐是指在密码哈希过程中引入一个随机生成的字符串,称为盐(salt),并将其与密码进行组合后再进行哈希计算。这个盐值会与每个用户的密码单独关联,并且将其存储在数据库中。加盐可以增加密码哈希的复杂度,提高密码的安全性,即使两个用户使用相同的密码,由于使用了不同的盐值,其哈希结果也会有所区别。这样即使黑客获得了哈希值,也很难通过暴力破解找到原始的密码。

盐值其实就是我们添加的一串随机字符串(这个值可以是固定值、随机数、uuid……),相同的字符串每次都会被加密为完全不同的字符串。

使用加盐加密时需要注意以下两点:

(1)短盐值(Short Slat)

如果盐值太短,攻击者可以预先制作针对所有可能的盐值的查询表。例如,如果盐值只有三个 ASCII 字符,那么只有 95x95x95=857,375 种可能性,加大了被攻击的可能性。还有,不要使用可预测的盐值,比如用户名,因为针对某系统用户名是唯一的且被经常用于其他服务。

(2)盐值复用(Salt Reuse)

在项目开发中,有时会遇到将盐值写死在程序里或者只有第一次是随机生成的,之后都会被重复使用,这种加盐方法是不起作用的。以登录密码为例,如果两个用户有相同的密码,那么他们就会有相同的哈希值,攻击者就可以使用反向查表法对每个哈希值进行字典攻击,使得该哈希值更容易被破解。

所以正确的加盐方法如下:

(1)盐值应该使用加密的安全伪随机数生成器( Cryptographically Secure Pseudo-Random Number Generator,CSPRNG )产生,比如 C 语言的 rand() 函数,这样生成的随机数高度随机、完全不可预测;

(2)盐值混入目标文本中,一起使用标准的加密函数进行加密;

(3)盐值要足够长(经验表明:盐值至少要跟哈希函数的输出一样长)且永不重复;

(4)盐值最好由服务端提供,前端取值使用。

我们一起SHA-256为例实现加盐加密。

安装js-sha256

pnpm install js-sha256

使用方法:

import { sha256 } from 'js-sha256';const generateSalt = () => {const randomBytes = new Uint8Array(16);crypto.getRandomValues(randomBytes);return Array.from(randomBytes, (byte) =>byte.toString(16).padStart(2, '0')).join('');
};
// 盐值
const salt = generateSalt();
// 假如一个密码
const password = 'admin123456';
// 盐值和密码进行组合
const saltedPassword = salt + password;
// 哈希计算
const hash = sha256(saltedPassword);
console.log(hash);

六、Web Cryptography API 


Web Cryptography API(WebCrypto API)是现代浏览器内置的一个加密标准,可以用于更安全的密码学操作。这个API允许我们在Web应用中实现加密、解密、数字签名等安全功能,而无需依赖第三方库或插件。

WebCrypto API是W3C的标准,它提供了一套低级别的接口用于执行各种密码学操作。这些接口包括:

生成和管理密钥:如RSA、AES和HMAC等。

加密和解密:支持多种对称和非对称加密算法。

摘要和哈希:如SHA-1、SHA-256和MD5等。

数字签名和验证:用于确保数据的完整性和来源真实性。

随机数生成:用于创建安全的随机数。

window.crypto.subtle就是SubtleCrypto接口,主流最新浏览器基本都已经支持该接口,并且一致,除了safari需要注意。

// fix safari crypto namespaceif (window.crypto && !window.crypto.subtle && window.crypto.webkitSubtle) {window.crypto.subtle = window.crypto.webkitSubtle; }/**
* Detect Web Cryptography API
* @return {Boolean} true, if success 
*/
function isWebCryptoAPISupported() {return 'crypto' in window && 'subtle' in window.crypto;
}


getRandomValues 同步方法,获取随机数,因为性能要求,这是一个伪随机数生成器(PRNG)。浏览器也通过添加系统级别的种子来提高熵(不确定性的量度),来满足密码学使用要求。

const size = 10;
const array = new Uint8Array(size);
window.crypto.getRandomValues(array);// print values to console
for (let i=0; i!==array.length; ++i) {console.log(array[i]);
}


SubtleCrypto接口:

encrypt、decrypt(加解密方法),算法支持:RSA-OAEP、AES-CTR、AES-CBC、AES-GCM、AES-CFB。

Sign、verify(签名验签发方法),算法支持:RSASSA-PKCS1-v1.5、RSA-PSS、ECDSA、AES-CMAC、HMAC。

Digest(摘要方法),算法支持:SHA-1、SHA-256、SHA-384、SHA-512。

GenerateKey:生成对称/非对称的密钥(CryptoKey对象)。

DeriveKey 和 deriveBits:从原密钥或是从伪随机函数生成的密码/口令中生成出一个密钥。

WrapKey 和 unwrapKey:用来保护在不安全信道或不可信环境存储的密钥的方法。

ImportKey 和 exportKey:导入密钥,转换再导出不同格式。

上面的方法都会返回Promise。

以下是使用WebCrypto API实现一个简单的加密,代码如下:

// 生成公钥和私钥
const keyPair = await window.crypto.subtle.generateKey({name: "RSA-OAEP", // 使用RSA-OAEP算法modulusLength: 2048, // 密钥长度为2048位publicExponent: new Uint8Array([1, 0, 1]), // 公共指数为65537hash: "SHA-256" // 哈希算法为SHA-256},true, // 生成可导出的密钥对["encrypt", "decrypt"] // 可用于加密和解密操作
);
keyPair.then(function(result) {// 处理生成的密钥对console.log(result.publicKey); // 打印公钥console.log(result.privateKey); // 打印私钥
}).catch(function(error) {// 处理错误console.error(error);
});
// 加密
async function encryptByRSA(message, publicKey) {// 将消息编码为Uint8Array格式const encodedMessage = new TextEncoder().encode(message);// 使用Web Crypto API的encrypt()方法对消息进行加密const encrypted = await window.crypto.subtle.encrypt({name: "RSA-OAEP" // 加密算法为RSA-OAEP},publicKey,  // 使用传入的公钥进行加密encodedMessage  // 要加密的消息);// 将加密后的数据转换为Base64编码的字符串return window.btoa(String.fromCharCode(...new Uint8Array(encrypted)));
}// 解密  使用RSA私钥对密文进行解密
async function decryptByRSA(ciphertext, privateKey) {// 将Base64编码的密文解码为Uint8Array格式const decodedCiphertext = Uint8Array.from(atob(ciphertext),c => c.charCodeAt(0));// 使用Web Crypto API的decrypt()方法对密文进行解密const decrypted = await window.crypto.subtle.decrypt({name: "RSA-OAEP" // 解密算法为RSA-OAEP},privateKey, // 使用传入的私钥进行解密decodedCiphertext // 要解密的密文);// 将解密后的数据转换为字符串return new TextDecoder().decode(decrypted);
}


七、总结

以上几种加密方法提供了基本的前端加密方法。大部分时候,为了确保数据的安全性,会将一些敏感数据(如手机号、身份证号、银行卡号等)发送到服务端,然后再服务端进行加密。前端加密并不能确保数据的安全性,因为客户端是暴露给用户的,任何混淆或加密措施都可能被绕过(前端加密防君子不放小人)。正确的做法是在保证数据传输过程中通过HTTPS等协议加密,并在服务端进行必要的安全校验和加密处理。

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

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

相关文章

【必会面试题】synchronized锁升级的过程

目录 Java中的synchronized关键字用于实现同步控制,以保护共享资源免受并发访问的影响。为了提高性能,特别是针对多线程环境中的锁机制,Java引入了锁升级的概念。锁升级的过程主要是为了减少锁操作的开销,根据竞争情况动态地调整锁…

正邦科技(day1)

1:充电桩工作了两个半小时,已用电量13度电(一般的话是一个小时7度电) 2:火线(红色,棕色),零线(蓝色) 3:充电桩工作了两个半小时&#…

【ARM+Codesys案例】RK3568 +Codesys 软PLC方案在电镀生产线的应用

1 电镀生产简介 电镀是一种比较重要的工艺,产品经过电镀工艺处理后,不仅产品质量获得提高,产品性能也会大幅度提高,同时延长了产品的使用时间。电镀生产线是指按一定的电镀生产工艺要求,将有关的各种电镀处理槽、电镀行车运动装置…

STM32_USART

1、USART简介 USART,即Universal Synchronous/Asynchronous Receiver/Transmitter,通用同步/异步收发器。USART是STM32内部集成的硬件外设,可根据数据寄存器的一个字节数据自动生成数据帧时序,从TX引脚发送出去,也可自…

香橙派 AIpro开发板开箱测评(代码开源)

前言:有幸能够收到一块梦寐以求的 AI 边缘计算开发板 OrangePi AIpro,非常感谢官方大大给予的宝贵机会。OrangePi AIpro是香橙派官方跟华为昇腾合作的新一代边缘计算产品,其使用华为昇腾 AI 技术路线,搭配集成图像处理器&#xff…

【C++题解】1133. 字符串的反码

问题:1133. 字符串的反码 类型:字符串 题目描述: 一个二进制数,将其每一位取反,称之为这个数的反码。下面我们定义一个字符的反码。 如果这是一个小写字符,则它和字符 a 的距离与它的反码和字符 z 的距离…

银行从业资格证初级计算题公式

单利本息和(利率固定,利息不叠加计算求和,常用于定期存款) 复利本息和(利率固定,利率与利息本金叠加计算求和,常用于某段范围内进行投资) 复利利率(计算利率不用涉及本金…

docker image分析利器之dive

dive是一个用于研究 Docker 镜像、层内容以及发现缩小 Docker/OCI 镜像大小方法的开源工具. 开源地址: dive github 为了有个直观的印象, 可以先看一下repo文档中的gif图: 安装 在Ubuntu/Debian系统下,可以使用deb包安装: DIVE_VERSION$(curl -sL "https:/…

推荐aeon,一个爆好用的 Python 库

今天为大家分享一个好用的 Python 库 - aeon Github地址:https://github.com/aeon-toolkit/aeon 在现代计算机科学和人工智能领域,处理时间序列数据是一个重要而复杂的任务。Python aeon库应运而生,它为开发者提供了强大的工具和方法来处理时…

使用虚拟卡注册亚马逊店铺亲测墨西哥、北美都可以亲测~~

这几天测试了使用虚拟信用卡注册墨西哥与北美站的店铺,成功下店,总有人说会被扫,其实去年12月费就有使用卡注册店铺,至今还是好的 当然也不是完全都没有可能店铺不会挂,挂的时候提供账单就好了,直接找客服…

Linux 一键部署alfresco 6

alfresco 前言 Alfresco是一个流行的企业级开源内容管理系统和协作平台。它提供了丰富的功能,包括文档管理、记录管理、协作工具、工作流管理、搜索和版本控制等。Alfresco还具有灵活的部署选项,可以作为本地部署的软件或云服务来使用。 该平台可以帮助组织管理和存储各种类…

【源码】Spring Data JPA原理解析之Repository执行过程及SimpleJpaRepository源码

Spring Data JPA系列 1、SpringBoot集成JPA及基本使用 2、Spring Data JPA Criteria查询、部分字段查询 3、Spring Data JPA数据批量插入、批量更新真的用对了吗 4、Spring Data JPA的一对一、LazyInitializationException异常、一对多、多对多操作 5、Spring Data JPA自定…

服务器数据恢复—EVA存储异常断电重启后虚拟机无法启动如何恢复数据?

服务器存储数据恢复环境: 某品牌EVA8400,服务器上安装VMware ESXi虚拟化平台,虚拟机的虚拟磁盘包括数据盘(精简模式)快照数据盘,部分虚拟机中运行oracle数据库和mysql数据库。 服务器存储故障&检测&…

c/c++内存操作函数

函数原型 void* memcpy( void* _Dst, void const* _Src, size_t _Size); // 内存拷贝 void* memmove( void* _Dst, void const* _Src, size_t _Size); // 内存移动 void* memset(void* _Dst, int _Val, size_t _Size); …

iOS内购欺诈漏洞

iOS内购欺诈漏洞 1.iOS内购欺诈漏洞概述2.伪造的凭证3.漏洞修复方案 1.iOS内购欺诈漏洞概述 黑产别的App上低价充值(比如1元)换取苹果真实凭证,再在目标App上下单高价(648元)商品,传入该凭证,如…

操作系统 - 输入/输出(I/O)管理

输入/输出(I/O)管理 考纲内容 I/O管理基础 设备:设备的基本概念,设备的分类,I/O接口 I/O控制方式:轮询方式,中断方式,DMA方式 I/O软件层次结构:中断处理程序,驱动程序,…

代码随想录算法训练营第四天| 24.两两交换链表中的节点、19.删除链表的倒数第N个节点、面试题 02.07. 链表相交、142.环形链表II

24.两两交换链表中的节点 给定一个链表,两两交换其中相邻的节点,并返回交换后的链表。你不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。 解题思路 很麻烦的一道题目,不是很理解。还是看视频文章才AC的。 解法1 …

一些硬件知识【九】

STM32CUBEMX配置教程(六)高级定时器单通道输出互补PWM波(带死区和刹车)_高级定时器死区刹车怎么与电机模块连接?-CSDN博客 MOS选型关键参数: 额定电压,额定电流,导通电阻,阀值电压…

新型高性能数据记录仪ETHOS 2

| 具有强大CPU性能的数据记录仪 IPETRONIK推出了一款新型高性能数据记录仪——ETHOS 2,作为ETHOS的第二代,它借助新型英特尔i7-9850HE处理器,实现了11,572的性能指数,从而能够快速有效应对CAN FD、LIN和以太网总线测量方面的日益…

Double数据精度导致的问题

早知道double的数据精度有限,所以在计算金钱等精细数据的时候,要用bigdecimal; 但是最近在计算一些客流量数据数据的时候,也发现,这种数据精度不足的情况,会不仅仅导致小数点级的计算结果出错 实在是&#…