import * as bcrypt from 'bcrypt'; /** * 密码值对象 * 包含密码验证规则和加密逻辑 */ export class Password { private static readonly MIN_LENGTH = 8; private static readonly MAX_LENGTH = 32; private static readonly SALT_ROUNDS = 12; private constructor(public readonly hash: string) {} /** * 从明文密码创建(会进行加密) */ static async create(plainPassword: string): Promise { Password.validatePlain(plainPassword); const hash = await bcrypt.hash(plainPassword, Password.SALT_ROUNDS); return new Password(hash); } /** * 从已加密的 hash 重建 */ static fromHash(hash: string): Password { return new Password(hash); } /** * 验证明文密码是否匹配 */ async verify(plainPassword: string): Promise { return bcrypt.compare(plainPassword, this.hash); } /** * 验证密码格式 */ private static validatePlain(password: string): void { if (password.length < Password.MIN_LENGTH) { throw new Error(`密码长度不能少于 ${Password.MIN_LENGTH} 位`); } if (password.length > Password.MAX_LENGTH) { throw new Error(`密码长度不能超过 ${Password.MAX_LENGTH} 位`); } // 至少包含一个字母和一个数字 if (!/[a-zA-Z]/.test(password) || !/\d/.test(password)) { throw new Error('密码必须包含字母和数字'); } } /** * 检查密码强度(仅验证格式,不加密) */ static checkStrength(password: string): { valid: boolean; message?: string } { try { Password.validatePlain(password); return { valid: true }; } catch (error) { return { valid: false, message: (error as Error).message }; } } toString(): string { return '[PROTECTED]'; } }