使用Bcrypt的NextJS登录接口密码校验始终返回false
问题:NextJS登录时bcrypt.compare始终返回false
我基于NextJS开发登录/注册功能,使用bcrypt实现密码哈希与校验。注册时能成功将哈希后的密码存入数据库,但登录调用bcrypt.compare()时,即便输入密码与数据库中的哈希密码匹配,校验结果始终返回false。问题出在这行代码:
const isPasswordMatched = await bcrypt.compare(password, user.password);
登录接口代码:api/auth/[...nextauth].ts
const authOptions: NextAuthOptions = { session: { strategy: "jwt", }, providers: [ CredentialsProvider({ async authorize(credentials, req) { await connectDB(); const { email, password }: Icredential = credentials; // 根据邮箱查找用户 const user = await User.findOne({ email: email }); if (user === null) { throw new Error('Cannot find user'); } // 校验输入密码与数据库密码是否匹配 // 问题所在行 const isPasswordMatched = await bcrypt.compare(password, user.password); console.log(`Comparing ${password} to ${user.password}`); console.log("match ?", isPasswordMatched); // 不匹配时抛出错误 if (!isPasswordMatched) { throw new Error('Invalid email or password'); } // 返回授权用户 return user; }, credentials: undefined }), ], }; export default NextAuth(authOptions);
注册接口代码:api/register
const registerHandler = async (req: NextApiRequest, res: NextApiResponse) => { if (req.method === "POST") { try { const { user: _regiUser } = req.body; console.log(_regiUser) // 检查用户是否已存在 await connectDB() const existingUser = await User.findOne({ email: _regiUser.email }).exec(); console.log("existingUser", existingUser); // 邮箱已被使用时抛出错误 if (existingUser) { throw new Error("Email already used"); } // 密码加密 const hashedPassword: string = await bcrypt.hashSync( _regiUser.password, 10 ); console.log("_regiUser.password", _regiUser.password, hashedPassword) console.log(hashedPassword) // 替换明文密码为加密后的密码 _regiUser.password = hashedPassword; console.log(_regiUser) // 将用户存入数据库 await User.create(_regiUser) res.end() } catch (e: any) { console.log(e.message) } } }; export default registerHandler;
数据库中已存入哈希密码:
解决方案
1. 检查是否存在双重哈希(最可能原因)
查看你的User模型代码,如果存在类似以下的pre('save')钩子,会在创建用户时自动对密码哈希:
userSchema.pre('save', async function(next) { if (this.isModified('password')) { this.password = await bcrypt.hash(this.password, 10); } next(); });
这种情况下,你在注册接口中手动调用hashSync会导致密码被哈希两次,数据库中存储的是哈希后的哈希值,自然无法通过原密码校验。
- 解决:要么删除注册接口中的手动哈希代码,让模型钩子自动处理;要么移除模型中的pre-save哈希逻辑,保留注册时的手动哈希。
2. 修正同步哈希方法的错误使用
注册代码中await bcrypt.hashSync(...)是错误写法,hashSync是同步方法,不需要添加await:
- 修正为同步写法:
const hashedPassword: string = bcrypt.hashSync(_regiUser.password, 10); - 或者推荐使用异步写法(更适合Node.js环境):
const hashedPassword: string = await bcrypt.hash(_regiUser.password, 10);
3. 检查数据库密码字段是否被截断
bcrypt生成的哈希值固定为60字符左右,如果MongoDB中User集合的password字段被设置了长度限制(比如maxLength < 60),会导致哈希值被截断,校验失败。
- 解决:确保
password字段为无长度限制的String类型(MongoDB默认String无限制,无需额外配置)。
4. 确认登录时正确获取密码字段
如果User模型中设置了password: { type: String, select: false },那么findOne查询默认不会返回password字段,导致user.password为undefined或错误值。
- 解决:查询时显式指定包含password字段:
const user = await User.findOne({ email: email }).select('+password');
内容的提问来源于stack exchange,提问作者kawa
相关产品推荐
相关产品推荐

