使用Passport+MongoDB修改密码前如何校验旧密码是否匹配
问题核心原因
- 你当前直接对比明文密码和数据库存储的哈希值,二者不可能相等,这是第一个核心错误
- 现有哈希生成逻辑存在错误:
update('I love cupcakes')的作用是传入待加密的明文内容,你写死了固定字符串,没有传入用户实际输入的密码,生成的哈希和用户密码完全无关 - 不需要单独定义LocalStrategy来校验旧密码,直接在用户模型封装校验方法即可
具体实现方案
1. 调整User模型(Mongoose示例)
const mongoose = require('mongoose'); const crypto = require('crypto'); // 全局加密密钥建议存在环境变量中,不要硬编码 const PASSWORD_SECRET = process.env.PASSWORD_SECRET || 'your_custom_secret_here'; const userSchema = new mongoose.Schema({ email: { type: String, required: true, unique: true }, // 存储密码哈希 passwordHash: { type: String, required: true }, // 每个用户单独的随机盐,提升安全性 passwordSalt: { type: String, required: true } // 其他用户字段... }); // 注册时生成密码哈希和盐的方法 userSchema.methods.setPassword = function(plainPassword) { // 生成16位随机盐 this.passwordSalt = crypto.randomBytes(16).toString('hex'); // 用 盐+明文密码 生成HMAC哈希 this.passwordHash = crypto.createHmac('sha256', PASSWORD_SECRET) .update(this.passwordSalt + plainPassword.normalize()) .digest('hex'); }; // 校验密码的方法 userSchema.methods.verifyPassword = function(plainPassword) { // 用同一个盐和密钥生成待对比的哈希 const hashToVerify = crypto.createHmac('sha256', PASSWORD_SECRET) .update(this.passwordSalt + plainPassword.normalize()) .digest('hex'); // 用时序安全的对比方法,防止时序攻击 return crypto.timingSafeEqual(Buffer.from(this.passwordHash), Buffer.from(hashToVerify)); }; module.exports = mongoose.model('User', userSchema);
2. 注册逻辑调整
注册时调用setPassword方法存储密码,不要使用原来写死update内容的代码:
// 注册接口示例 app.post('/register', async (req, res) => { const { email, password } = req.body; const newUser = new User({ email }); // 生成密码哈希和盐 newUser.setPassword(password); await newUser.save(); // 后续注册逻辑... });
注意:如果你已经有存量注册用户,之前的注册逻辑生成的哈希和用户实际密码无关,存量用户需要走密码重置流程才能正常使用密码校验功能。
3. 修改密码接口逻辑调整
直接调用user对象的verifyPassword方法校验旧密码即可:
app.post('/update-password', async (req, res) => { // 先判断新密码和确认密码是否一致 if (req.body.newPassword.normalize() !== req.body.confirmPassword.normalize()) { // 处理新密码不一致的情况 return res.redirect('/profile?error=密码不一致'); } // 校验旧密码是否正确 if (!req.user.verifyPassword(req.body.password)) { // 处理旧密码错误的情况 return res.redirect('/profile?error=旧密码错误'); } // 旧密码校验通过,更新为新密码 req.user.setPassword(req.body.newPassword); await req.user.save(); res.redirect('/profile?success=密码修改成功'); });
可选优化:使用bcrypt简化流程
如果不想自己处理哈希和盐的逻辑,可以直接用bcrypt库,自动处理加盐和哈希生成,代码更简洁:
- 安装依赖:
npm install bcrypt - 调整模型方法:
const bcrypt = require('bcrypt'); userSchema.methods.setPassword = async function(plainPassword) { // 自动生成盐并哈希,10是加盐rounds,数值越高安全性越高、速度越慢 this.passwordHash = await bcrypt.hash(plainPassword.normalize(), 10); }; userSchema.methods.verifyPassword = async function(plainPassword) { return await bcrypt.compare(plainPassword.normalize(), this.passwordHash); };
注意bcrypt的方法是异步的,调用时需要添加await关键字。
内容的提问来源于stack exchange,提问作者VortexSensei
相关产品推荐
相关产品推荐

