Files
soul/lib/password.ts

57 lines
1.8 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* 密码哈希与校验(仅用于 Web 用户注册/登录,与后台管理员密码无关)
* 使用 Node crypto.scrypt存储格式 saltHex:hashHex兼容旧明文密码
*/
import { scryptSync, timingSafeEqual, randomFillSync } from 'crypto'
const SALT_LEN = 16
const KEYLEN = 32
function bufferToHex(buf: Buffer): string {
return buf.toString('hex')
}
function hexToBuffer(hex: string): Buffer {
return Buffer.from(hex, 'hex')
}
/**
* 对明文密码做哈希,存入数据库
* 格式: saltHex:hashHex约 97 字符,适配 VARCHAR(100)
* 与 verifyPassword 一致:内部先 trim保证注册/登录/重置用同一套规则
*/
export function hashPassword(plain: string): string {
const trimmed = String(plain).trim()
const salt = Buffer.allocUnsafe(SALT_LEN)
randomFillSync(salt)
const hash = scryptSync(trimmed, salt, KEYLEN, { N: 16384, r: 8, p: 1 })
return bufferToHex(salt) + ':' + bufferToHex(hash)
}
/**
* 校验密码支持新格式salt:hash与旧明文兼容历史数据
* 与 hashPassword 一致:对输入先 trim 再参与校验
*/
export function verifyPassword(plain: string, stored: string | null | undefined): boolean {
const trimmed = String(plain).trim()
if (stored == null || stored === '') {
return trimmed === ''
}
if (stored.includes(':')) {
const [saltHex, hashHex] = stored.split(':')
if (!saltHex || !hashHex || saltHex.length !== SALT_LEN * 2 || hashHex.length !== KEYLEN * 2) {
return false
}
try {
const salt = hexToBuffer(saltHex)
const expected = hexToBuffer(hashHex)
const derived = scryptSync(trimmed, salt, KEYLEN, { N: 16384, r: 8, p: 1 })
return derived.length === expected.length && timingSafeEqual(derived, expected)
} catch {
return false
}
}
return trimmed === stored
}