我正在尝试在 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 返回一个空对象。我意识到我的用户架构不包含密码字段,因此我尝试添加该字段,但随后注册路由停止工作。
您已正确确定您的架构中需要一个
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" });
}