Files

38 lines
1.3 KiB
JavaScript

/**
* 使用浏览器安全随机源生成密码:至少含小写、大写和数字各一位,排除易混字符。
* 即使要求长度小于 3,三个必选字符也会保留;安全随机源不可用时明确失败,不降级。
*/
function e(e = 12) {
const source = globalThis.crypto;
if (typeof source?.getRandomValues !== `function`)
throw Error(`当前环境不支持安全随机密码生成`);
const buffer = new Uint32Array(1);
// 只接受能均分为 upperBound 份的区间,避免直接取模让某些字符/洗牌位置概率偏高。
function randomIndex(upperBound) {
const limit = 2 ** 32 - (2 ** 32 % upperBound);
do source.getRandomValues(buffer);
while (buffer[0] >= limit);
return buffer[0] % upperBound;
}
let t = (e) => e[randomIndex(e.length)],
n = [
t(`abcdefghjkmnpqrstuvwxyz`),
t(`ABCDEFGHJKMNPQRSTUVWXYZ`),
t(`23456789`),
],
r = Array.from(
{
length: Math.max(0, e - n.length),
},
() => t(`abcdefghjkmnpqrstuvwxyzABCDEFGHJKMNPQRSTUVWXYZ23456789`),
),
i = [...n, ...r];
for (let e = i.length - 1; e > 0; e--) {
// Fisher–Yates 与字符选取共用无偏采样,不能在打乱阶段重新引入非安全随机源。
let t = randomIndex(e + 1);
[i[e], i[t]] = [i[t], i[e]];
}
return i.join(``);
}
export { e as t };