bcrypt 在测试我的登录路由(Express、Mongoose、jwt)时比较返回 false

问题描述 投票:0回答:1

我正在尝试在 NodeJs、Express、Mongoose 等中测试我的登录路由。我的注册路由正在工作,但登录路由返回错误消息。我的代码如下

用户架构

//1. require mongoose
const mongoose=require('mongoose')
//2. create schema
const User=new mongoose.Schema({
    firstName:{
        type:String,
        required:true,
    },
    lastName:{
        type:String,
        required:false,
    },
    email:{
        type:String,
        required:true,
        unique:true
    },
    username:{
        type:String,
        required:true,
        unique:true
    },
    likedSongs:{//will be changed to array later
        type:String,
        default:'',
    },
    likedPlaylists:{
        type:String,
        default:''
    },
    subscribedArtists:{
        type:String,
        default:'',
    }
})
//export schema
module.exports=mongoose.model("User",User)

用于注册和登录的身份验证路由(这些路由导入到我的 index.js 文件中,如下所示)

const express=require('express')
const router=express.Router()
const User=require('../models/User')
const bcrypt=require('bcrypt')
const {getToken}=require('../utils/helpers')
const passport=require('passport')

router.post('/register', async (req,res)=>{
    //This code is run when /register is called to post a new user
    //req.body will be json in the form firstname, lastname, email, username
    const {firstName, lastName, email, username, password}=req.body
    //Does a user with this email already exist? yes>error
    const user= await User.findOne({email}) //email in User schema is equal to req.body email
    if (user) { //user true should make an error
        //status code by default is 200 but we want to make an error
        return res
            .status(403)
            .json({error: 'A user with this email address already exists'})
    } else{//the user does not exist so create a new user
        //do not store password in plain text, convert to a hash (not brown)
        const hashedPw=bcrypt.hash(password,10)
        const newUserData={firstName, lastName, email, username, password: hashedPw}
        const newUser=await User.create(newUserData)
        //create token to return to the user
        const token= await getToken(email,newUser)
        //return result to user
        const userToReturn={...newUser.toJSON(),token}
        delete userToReturn.password
        return res.status(200).json(userToReturn)
    }
})

router.post('/login', async (req,res)=>{
    //Get email & pw from user from req.body
    const {username,password}=req.body
    // //Check if user with given email exists if not credentials are invalid
    const user= await User.findOne({username})
    if (!user) {
        res.status(401).json({error:'your credentials are invalid'})
    }
    try {
        await bcrypt.compare(password, user.password, (err, result) => {
            if (err) {
                return res.status(401).json({
                    success:false,
                    message:'Authentication Failed'
                })
            } 
            if(result) {
                const token = getToken(user)
                const loggedInUser={
                    token,
                    username:user.username
                }
                return res.status(200).json({
                    success:true,
                    message:'Login Successful! Redirecting...',
                    loggedInUser
                })
            }
            return res.status(401).json({ success: false, message: "Authentication Failed" })
        })
    } catch (error) {
        res.status(401).json({ success: false, message: "Authentication Failed" });
    }

获取Token函数

const jwt=require('jsonwebtoken')
require('dotenv').config()

exports={}

exports.getToken= async user=>{
    const token=jwt.sign(
        {identifier:user._id},
        process.env.JWT_SECRET)
    return token
}

module.exports=exports

我逐行测试,发现 user.password 返回一个空对象。我意识到我的用户架构不包含密码字段,因此我尝试添加该字段,但随后注册路由停止工作。

node.js express mongoose jwt bcrypt
1个回答
0
投票

您已正确确定您的架构中需要一个

password
属性,因此请保留它。
bcrypt.hash()
函数创建一个 60 个字符长度的字符串,因此如果您想为密码实现
minLength
maxLength
内置验证器,则需要在散列密码之前完成此操作。这可以使用
pre('save')
hooks 来实现。

根据 bcrypt docs

如果您在服务器上使用 bcrypt,我们建议使用异步 API。 Bcrypt 散列是 CPU 密集型的,这将导致同步 API 阻塞事件循环并阻止您的应用程序为任何入站请求或事件提供服务。异步版本使用不会阻塞主事件循环的线程池。

由于您的

router.post('/register')
回调函数被标记为
async
,请将哈希更改为:

const hashedPw = await bcrypt.hash(password,10) 

您的代币未正确创建。在

/register
路线内,您将
email
newUser
传递给
getToken
函数。但是你的
getToken
函数只接受一个参数(
user
),从它的外观来看,你似乎将
email
作为参数传递,因为
newUser
将被忽略:

const token= await getToken(email,newUser) //< Two parameters

exports.getToken= async user=>{ //< One parameter
    const token=jwt.sign(
        {identifier:user._id}, //< There is no user here so user._id will be undefined
        process.env.JWT_SECRET)
    return token
}

在您的

/login
路线中,您可能还需要考虑使用
async/await
作为您的
bcrypt.compare
,因为您似乎有混合回调和
await
在那里进行。更清晰的流程控制会更好,并且考虑到您的
/login
函数也标记为
async
,实际上不需要在代码中使用回调。它看起来像这样:

try {
    if(await bcrypt.compare(password, user.password)){
        const token = await getToken(user); //< You need to use await here too
        const loggedInUser={
            token,
            username:user.username
        }
        return res.status(200).json({
            success:true,
            message:'Login Successful! Redirecting...',
            loggedInUser
        });
    } else {
        return res.status(401).json({
            success: false,
            message: 'Authentication Failed'
        });
    }
} catch (error) {
    console.log(error);
    res.status(401).json({ success: false, message: "Authentication Failed" });
}
© www.soinside.com 2019 - 2024. All rights reserved.