更新完善页面
This commit is contained in:
@@ -0,0 +1,107 @@
|
||||
/**
|
||||
* 密码加密和验证工具函数
|
||||
* @module services/utils/password
|
||||
*/
|
||||
|
||||
const crypto = require('crypto')
|
||||
|
||||
const N = 16384
|
||||
const r = 8
|
||||
const p = 1
|
||||
const KEYLEN = 64
|
||||
const SALT_LEN = 16
|
||||
const MD5_LEN = 32
|
||||
|
||||
/**
|
||||
* 计算 MD5 哈希
|
||||
* @param {string} str - 输入字符串
|
||||
* @returns {string} MD5 哈希值(十六进制)
|
||||
*/
|
||||
function md5(str) {
|
||||
return crypto.createHash('md5').update(str).digest('hex')
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用 scrypt 计算哈希
|
||||
* @param {string} password - 密码
|
||||
* @param {Buffer} [saltBuf] - 可选的 salt
|
||||
* @returns {Object} { salt, hash }
|
||||
*/
|
||||
function scryptHash(password, saltBuf) {
|
||||
const salt = saltBuf || crypto.randomBytes(SALT_LEN)
|
||||
const derived = crypto.scryptSync(String(password), salt, KEYLEN, { N, r, p, maxmem: 64 * 1024 * 1024 })
|
||||
return { salt, hash: derived }
|
||||
}
|
||||
|
||||
/**
|
||||
* 哈希密码(使用 scrypt)
|
||||
* @param {string} password - 密码
|
||||
* @returns {string} 编码后的密码哈希
|
||||
*/
|
||||
function hashPassword(password) {
|
||||
const { salt, hash } = scryptHash(password)
|
||||
const saltB64 = salt.toString('base64').replace(/=+$/, '')
|
||||
const hashB64 = hash.toString('base64').replace(/=+$/, '')
|
||||
return `scrypt$${N}$${r}$${p}$${saltB64}$${hashB64}`
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证 scrypt 密码
|
||||
* @param {string} password - 密码
|
||||
* @param {string} encoded - 编码后的密码哈希
|
||||
* @returns {boolean} 是否验证通过
|
||||
*/
|
||||
function verifyScrypt(password, encoded) {
|
||||
try {
|
||||
const parts = encoded.split('$')
|
||||
if (parts.length !== 6 || parts[0] !== 'scrypt') return false
|
||||
const saltB64 = parts[4]
|
||||
const expectedB64 = parts[5]
|
||||
const salt = Buffer.from(saltB64, 'base64')
|
||||
const expected = Buffer.from(expectedB64, 'base64')
|
||||
const { hash } = scryptHash(password, salt)
|
||||
if (hash.length !== expected.length) return false
|
||||
return crypto.timingSafeEqual(hash, expected)
|
||||
} catch {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证密码(支持 scrypt 和旧版 MD5)
|
||||
* @param {string} password - 密码
|
||||
* @param {string} stored - 存储的密码哈希
|
||||
* @returns {boolean} 是否验证通过
|
||||
*/
|
||||
function verifyPassword(password, stored) {
|
||||
if (!stored) return false
|
||||
if (stored.startsWith('scrypt$')) return verifyScrypt(password, stored)
|
||||
if (/^[a-f0-9]{32}$/i.test(stored)) return md5(password).toLowerCase() === stored.toLowerCase()
|
||||
return false
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查是否是旧版 MD5 哈希
|
||||
* @param {string} stored - 存储的密码哈希
|
||||
* @returns {boolean} 是否是旧版哈希
|
||||
*/
|
||||
function isLegacyHash(stored) {
|
||||
return stored && /^[a-f0-9]{32}$/i.test(stored)
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查是否需要重新哈希密码
|
||||
* @param {string} stored - 存储的密码哈希
|
||||
* @returns {boolean} 是否需要重新哈希
|
||||
*/
|
||||
function needsRehash(stored) {
|
||||
return !stored || !stored.startsWith('scrypt$')
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
hashPassword,
|
||||
verifyPassword,
|
||||
isLegacyHash,
|
||||
needsRehash,
|
||||
md5
|
||||
}
|
||||
Reference in New Issue
Block a user