You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

55 lines
1.5 KiB

import bcrypt from "bcryptjs";
const SALT_ROUNDS = 12;
const PASSWORD_HISTORY_SIZE = 5;
export function hashPassword(password: string): Promise<string> {
return bcrypt.hash(password, SALT_ROUNDS);
}
export async function verifyPassword(
password: string,
hashedPassword: string
): Promise<boolean> {
return bcrypt.compare(password, hashedPassword);
}
export async function isPasswordInHistory(
password: string,
history: string[]
): Promise<boolean> {
if (history.length === 0) return false;
for (const h of history) {
if (await bcrypt.compare(password, h)) return true;
}
return false;
}
export function addPasswordToHistory(
newHash: string,
history: string[]
): string[] {
const updated = [newHash, ...history];
return updated.slice(0, PASSWORD_HISTORY_SIZE);
}
export interface PasswordStrengthResult {
valid: boolean;
errors: string[];
}
export function validatePasswordStrength(password: string): PasswordStrengthResult {
const errors: string[] = [];
if (password.length < 8) errors.push("至少8个字符");
if (!/[A-Z]/.test(password)) errors.push("需包含大写字母");
if (!/[a-z]/.test(password)) errors.push("需包含小写字母");
if (!/[0-9]/.test(password)) errors.push("需包含数字");
if (!/[!@#$%^&*()_+\-=\[\]{};':"\\|,.<>\/?]/.test(password))
errors.push("需包含特殊字符");
return { valid: errors.length === 0, errors };
}
export function isLocked(lockoutUntil: Date | null): boolean {
if (!lockoutUntil) return false;
return Date.now() < lockoutUntil.getTime();
}