getRandomValues 是 JavaScript 中用于生成加密安全的随机数的 API,属于 Crypto 接口(Web Crypto API 的一部分),相比传统的 Math.random(),它能生成更安全、不可预测的随机数,适用于密码学场景(如生成密钥、令牌、验证码等)。
一、核心特点与优势
1. 对比 Math.random() 的优势
| 特性 | Math.random() | crypto.getRandomValues() |
|---|---|---|
| 安全性 | 弱(伪随机,可预测,基于种子) | 强(加密安全,不可预测,基于硬件 / 系统随机源) |
| 适用场景 | 普通随机场景(如随机排序、抽奖) | 安全敏感场景(如生成密钥、令牌、验证码) |
| 输出范围 | [0, 1) 浮点数 | 整数(按指定类型和范围生成) |
2. 关键特性
- 加密安全:符合密码学安全标准(如 NIST 规范),随机数不可预测、不可重现,能抵御暴力破解。
- 多类型支持:可生成 8 位 / 16 位 / 32 位整数(有符号 / 无符号)、64 位浮点数等。
- 浏览器原生支持:现代浏览器(Chrome 37+、Firefox 34+、Safari 7.1+、Edge 12+)均支持,无需额外库。
二、基本语法
// 语法:crypto.getRandomValues(typedArray)
const typedArray = new TypedArray(length);
crypto.getRandomValues(typedArray);
改造一下
Math.randomValue = function () {
return self.crypto.getRandomValues(new Uint32Array(1))[0];
};
-
参数:
typedArray- 一个定型数组(TypedArray) (如Uint8Array、Uint32Array等),用于存储生成的随机数。 -
返回值:修改后的定型数组(随机数会直接写入输入的数组,无需额外接收返回值)。
-
支持的定型数组类型:
- 整数类型:
Uint8Array(8 位无符号)、Int8Array(8 位有符号)、Uint16Array(16 位无符号)、Int16Array(16 位有符号)、Uint32Array(32 位无符号)、Int32Array(32 位有符号)。 - 浮点数类型:
Float32Array、Float64Array(生成 [0, 1) 范围的浮点数)。
- 整数类型:
三、常用示例
1. 生成指定范围的随机整数(最常用)
需求:生成 [min, max] 之间的加密安全随机整数(包含边界)。
/**
* 生成 [min, max] 范围内的加密安全随机整数
* @param {number} min - 最小值(整数)
* @param {number} max - 最大值(整数)
* @returns {number} 随机整数
*/
function getSecureRandomInt(min, max) {
// 校验参数:确保 min <= max 且为整数
if (!Number.isInteger(min) || !Number.isInteger(max) || min > max) {
throw new Error('min 和 max 必须是整数,且 min <= max');
}
// 计算范围大小
const range = max - min + 1;
// 若范围为 1,直接返回 min(无需生成随机数)
if (range === 1) return min;
// 选择合适的定型数组类型(范围越小,类型越节省内存)
let typedArray;
if (range <= 256) {
typedArray = new Uint8Array(1); // 8位无符号:0-255
} else if (range <= 65536) {
typedArray = new Uint16Array(1); // 16位无符号:0-65535
} else {
typedArray = new Uint32Array(1); // 32位无符号:0-4294967295
}
// 生成随机数(循环确保结果在范围内,避免偏倚)
let randomValue;
do {
crypto.getRandomValues(typedArray);
randomValue = typedArray[0];
} while (randomValue >= Math.floor(2 ** (typedArray.BYTES_PER_ELEMENT * 8) / range) * range);
// 映射到目标范围
return min + (randomValue % range);
}
// 测试:生成 1-100 之间的随机整数
console.log(getSecureRandomInt(1, 100)); // 如 37、89 等(每次不同)
// 测试:生成 0-9 之间的随机数(验证码常用)
console.log(getSecureRandomInt(0, 9)); // 如 5、2 等
- 核心逻辑:通过循环过滤掉超出「范围整数倍」的随机数,避免因定型数组最大值不是范围的整数倍导致的概率偏倚(如用 8 位随机数生成 0-10 时,0-5 出现概率略高)。
2. 生成加密安全的随机字符串(如令牌、验证码)
需求:生成 16 位由字母 + 数字组成的随机令牌。
/**
* 生成指定长度的加密安全随机字符串(字母+数字)
* @param {number} length - 字符串长度
* @returns {string} 随机字符串
*/
function getSecureRandomString(length = 16) {
if (!Number.isInteger(length) || length < 1) {
throw new Error('长度必须是大于 0 的整数');
}
// 字符池:包含大小写字母和数字(共 62 个字符)
const charset = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
const charsetLength = charset.length;
// 生成足够的随机整数(每个整数对应一个字符)
const typedArray = new Uint32Array(length);
crypto.getRandomValues(typedArray);
// 映射随机数到字符池
let result = '';
for (let i = 0; i < length; i++) {
const randomIndex = typedArray[i] % charsetLength;
result += charset[randomIndex];
}
return result;
}
// 测试:生成 16 位随机令牌
console.log(getSecureRandomString(16)); // 如 "x7Z9k2PqR3sT8vU1"(每次不同)
// 测试:生成 6 位数字验证码
function getSecureVerifyCode(length = 6) {
const charset = '0123456789';
const typedArray = new Uint32Array(length);
crypto.getRandomValues(typedArray);
return Array.from(typedArray, num => charset[num % 10]).join('');
}
console.log(getSecureVerifyCode()); // 如 "385271"(每次不同)
3. 生成 [0, 1) 范围的加密安全浮点数
类似 Math.random(),但更安全:
function getSecureRandomFloat() {
const typedArray = new Float64Array(1);
crypto.getRandomValues(typedArray);
return typedArray[0]; // 范围 [0, 1)
}
console.log(getSecureRandomFloat()); // 如 0.3456789012345678(加密安全)
四、注意事项
- 参数必须是定型数组:不能直接传递普通数组(如
[]),必须使用Uint8Array等定型数组,否则会抛出TypeError。 - 避免范围偏倚:生成指定范围整数时,若定型数组的最大值不是「范围大小」的整数倍,需通过循环过滤超出部分(如示例 1 中的
do-while逻辑),否则会导致部分数值出现概率更高。 - 浏览器兼容性:现代浏览器均支持,但 IE 11 及以下不支持(需降级为
Math.random()或使用第三方库)。 - 不能用于大量数据:
getRandomValues有性能限制(单次生成的数组长度不宜过大,通常建议不超过 65536 个元素),大量随机数生成需分批处理。
五、适用场景
- 密码学相关:生成加密密钥、会话令牌(Session ID)、JWT 密钥、加密盐(Salt)。
- 安全验证:生成手机验证码、邮箱验证码、图形验证码。
- 敏感随机场景:抽奖(避免作弊)、随机分配敏感资源(如红包金额)。
普通非敏感场景(如随机排序、随机展示内容)仍可使用 Math.random(),但涉及安全的场景必须使用 getRandomValues。
总结
crypto.getRandomValues() 是生成加密安全随机数的标准 API,核心优势是 “不可预测、无偏倚”,适用于所有安全敏感场景。使用时需注意:
- 必须传递定型数组存储结果;
- 生成指定范围整数时需避免偏倚;
- 现代浏览器支持,IE 需降级。
日常开发中,若需 “安全随机”,优先使用该 API 而非 Math.random()。
Math.random的底层实现原理
Math.random() 函数返回一个范围0-1的伪随机浮点数,其在 V8 中的实现原理是这样的:
为了保证足够的性能,Math.random() 随机数并不是实时生成的,而是直接生成一组随机数(64个),并放在缓存中。
当这一组随机数取完之后再重新生成一批,放在缓存中。
由于 Math.random() 的底层算法是公开的(xorshift128+ 算法),V8 源码可见,因此,是可以使用其他语言模拟的,这就导致,如果攻击者知道了当前随机生成器的状态,那就可以知道缓存中的所有随机数,那就很容易匹配与破解。
例如抽奖活动,使用 Math.random() 进行随机,那么就可以估算出一段时间内所有的中奖结果,从而带来非常严重且致命的损失。此时应该使用 getRandomValues() 方法。
getRandomValues() 方法的底层实现是没有缓存的,随机数都是实时生成的,因此,性能上是要比 Math.random() 差的,因此,如果是高并发的场景,同时随机数仅仅是用做随机,与安全和金钱不相关,请使用 Math.random() 而不是 getRandomValues()。
就 Web 前端而言,必须要使用 getRandomValues() 方法的场景很少,不过由于纯前端几乎不存在所谓的高并发,因此,你使用 getRandomValues() 方法也是可以的,有装逼的作用。