You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用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;

数据库中已存入哈希密码:
MongoDB中的哈希密码


解决方案

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.31 10:55:50