- 设置页新增「账号安全」面板,当前登录用户校验原密码后可修改自己的密码 - 新增 PUT /api/user/password 接口,BCrypt 校验原密码,新旧相同与长度非法均拒绝 - 新增随机密码工具,一键生成 12 位大小写字母与数字组合密码(排除易混淆字符) - 随机密码明文展示并支持复制,非安全上下文回退 execCommand 方案 - 新增账号安全相关中英日三语文案 - AI 查询与统计结果新增 untagged/uncategorized 字段,提示词同步说明互斥规则 - 统计面板与交易列表支持「无标签」筛选项,AI 结果可回填无标签/未分类条件 - 备注已隐藏的交易不参与备注关键字搜索,避免隐藏内容被检索命中 - 交易列表操作列将「编辑」调整至「详情」之前
50 lines
1.7 KiB
TypeScript
50 lines
1.7 KiB
TypeScript
// 随机密码工具:生成强度较高且便于输入的密码
|
|
// 字符集排除易混淆字符(0/O/o/1/l/I),避免用户手动输入时出错
|
|
const UPPER = 'ABCDEFGHJKLMNPQRSTUVWXYZ'
|
|
const LOWER = 'abcdefghijkmnpqrstuvwxyz'
|
|
const DIGIT = '23456789'
|
|
const CHARSET = UPPER + LOWER + DIGIT
|
|
|
|
/** 生成指定长度的随机密码,默认 12 位,保证同时包含大小写字母与数字 */
|
|
export function generateRandomPassword(length = 12): string {
|
|
const size = Math.max(6, length)
|
|
const chars: string[] = [randomChar(UPPER), randomChar(LOWER), randomChar(DIGIT)]
|
|
// 补齐剩余位数后打乱,避免固定位置出现固定字符类型
|
|
while (chars.length < size) {
|
|
chars.push(randomChar(CHARSET))
|
|
}
|
|
return shuffle(chars).join('')
|
|
}
|
|
|
|
/** 从字符集中安全随机取一个字符 */
|
|
function randomChar(charset: string): string {
|
|
return charset[secureRandomInt(charset.length)]
|
|
}
|
|
|
|
/** 使用 crypto 生成 [0, max) 的随机整数,crypto 不可用时回退 Math.random */
|
|
function secureRandomInt(max: number): number {
|
|
if (typeof crypto !== 'undefined' && typeof crypto.getRandomValues === 'function') {
|
|
// 取整倍数区间拒绝采样,避免取模带来的分布偏差
|
|
const limit = Math.floor(0xffffffff / max) * max
|
|
const buffer = new Uint32Array(1)
|
|
let value = 0
|
|
do {
|
|
crypto.getRandomValues(buffer)
|
|
value = buffer[0]
|
|
} while (value >= limit)
|
|
return value % max
|
|
}
|
|
return Math.floor(Math.random() * max)
|
|
}
|
|
|
|
/** Fisher-Yates 洗牌 */
|
|
function shuffle(arr: string[]): string[] {
|
|
for (let i = arr.length - 1; i > 0; i--) {
|
|
const j = secureRandomInt(i + 1)
|
|
const tmp = arr[i]
|
|
arr[i] = arr[j]
|
|
arr[j] = tmp
|
|
}
|
|
return arr
|
|
}
|