Passport初始化后req.user未正确返回数据库用户信息问题
问题:Passport认证后req.user返回Query对象而非用户信息
我使用Mongoose连接MongoDB Atlas集群,搭配ExpressJS和NodeJS搭建登录认证系统,登录并完成Passport初始化后,req.user未返回数据库中存储的用户信息,而是返回了一个Query对象。已确认Session创建正常(console.log(req.sessionID)能返回会话ID),但req.session.user的输出结果与req.user一致。
预期req.user应返回如下格式的用户信息:
[ { _id: 64256e27f9e4e46834d7106b, username: 'w', email: 'w@w', password: '$2b$10$36eN7DN9d5H0r6DcSgL68OdL.ejNLPzD5qn0S.WTyoSbYCniNlKsS', role: 'basic', __v: 0 } ]
实际console.log(req.user)输出:
Query { _mongooseOptions: {}, _transforms: [], _hooks: Kareem { _pres: Map(0) {}, _posts: Map(0) {} }, _executionCount: 0, mongooseCollection: NativeCollection { collection: Collection { s: [Object] }, Promise: [Function: Promise], modelName: 'User', _closed: false, opts: { autoIndex: true, autoCreate: undefined, schemaUserProvidedOptions: {}, capped: false, Promise: [Function: Promise], '$wasForceClosed': undefined }, name: 'users', collectionName: 'users', conn: NativeConnection { base: [Mongoose], collections: [Object], models: [Object], config: {}, replica: false, options: null, otherDbs: [], relatedDbs: {}, states: [Object: null prototype], _readyState: 1, _closeCalled: undefined, _hasOpened: true, plugins: [], id: 0, _queue: [], _listening: false, _connectionString: 'mongodb+srv://davikzetus:Rawe159456789@clustertesting.8kipjjz.mongodb.net/test?retryWrites=true&w=majority', _connectionOptions: [Object], client: [MongoClient], '$initialConnection': [Promise], _events: [Object: null prototype], _eventsCount: 1, name: 'test', host: 'ac-krjjmvv-shard-00-00.8kipjjz.mongodb.net', port: 27017, user: 'davikzetus', pass: '***********', db: [Db] }, queue: [], buffer: false, emitter: EventEmitter { _events: [Object: null prototype] {}, _eventsCount: 0, _maxListeners: undefined, [Symbol(kCapture)]: false } }, model: Model { User }, schema: Schema { obj: { username: [Object], email: [Object], password: [Object], role: [Object] }, paths: { username: [SchemaString], email: [SchemaString], password: [SchemaString], role: [SchemaString], _id: [ObjectId], __v: [SchemaNumber] }, aliases: {}, subpaths: {}, virtuals: { id: [VirtualType] }, singleNestedPaths: {}, nested: {}, inherits: {}, callQueue: [], _indexes: [], methods: {}, methodOptions: {}, statics: {}, tree: { username: [Object], email: [Object], password: [Object], role: [Object], _id: [Object], __v: [Function: Number], id: [VirtualType] }, query: {}, childSchemas: [], plugins: [ [Object], [Object], [Object], [Object], [Object], [Object] ], '$id': 1, mapPaths: [], s: { hooks: [Kareem] }, _userProvidedOptions: {}, options: { typePojoToMixed: true, typeKey: 'type', id: true, noVirtualId: false, _id: true, noId: false, validateBeforeSave: true, read: null, shardKey: null, autoIndex: null, minimize: true, discriminatorKey: '__t', optimisticConcurrency: false, versionKey: '__v', capped: false, bufferCommands: true, strictQuery: false, strict: true, pluralization: true }, '$globalPluginsApplied': true, _requiredpaths: [ 'role', 'password', 'email', 'username' ] }, op: 'find', options: {}, _conditions: { id: '64256e27f9e4e46834d7106b' }, _fields: undefined, _update: undefined, _path: undefined, _distinct: undefined, _collection: NodeCollection { collection: NativeCollection { collection: [Collection], Promise: [Function: Promise], modelName: 'User', _closed: false, opts: [Object], name: 'users', collectionName: 'users', conn: [NativeConnection], queue: [], buffer: false, emitter: [EventEmitter] }, collectionName: 'users' }, _traceFunction: undefined, '$useProjection': true }
问题代码
passport-config.js
const LocalStrategy = require('passport-local').Strategy const bcrypt = require('bcrypt') const User = require('/Work Archive/LapTrinhWeb/FINALVFINAL/models/user') async function initialize(passport, getUserByEmail, getUserById){ const athenticateUser = async (email, password, done) => { const user = await getUserByEmail(email) if (user.length == 0){ console.log(user._id) return done(null, false, {message : 'no user with that email'}) } try{ if( await bcrypt.compare(password, user[0].password)){ return done(null, user) } else{ return done(null,false,{message: 'password incorrect'}) } } catch (err) { return done(err) } } passport.use(new LocalStrategy({ usernameField: 'email'}, athenticateUser)) passport.serializeUser((user , done) => done(null, user[0].id)) passport.deserializeUser((id,done) => { done(null, getUserById(id)) }) } module.exports = initialize
server.js
const express = require('express') const app = express() const expressLayouts = require('express-ejs-layouts') const bodyParser = require('body-parser') const methodOverride = require('method-override') const bcrypt = require('bcrypt') const passport = require('passport') const flash = require('express-flash') const session = require('express-session') const {checkAuthenticated, checkNotAuthenticated} = require('./authenticates.js') const initializePassport = require('./passport-config') initializePassport(passport, email => User.find({email : email}), id => User.find({id:id}) ) app.use(session({ secret : process.env.SESSION_SECRET, resave : false, saveUninitialized : false })) app.use(passport.initialize()) app.use(passport.session()) app.get('/', checkAuthenticated, (req, res) => { res.render('login') }) app.get('/login', checkNotAuthenticated, (req, res) => { res.render('login') }) app.post('/login', checkNotAuthenticated, passport.authenticate('local', { successRedirect: '/', failureRedirect: '/login', failureFlash: true }), (req,res) => { console.log(req.user) }) app.get('/register', checkNotAuthenticated, (req, res) => { res.render('register.ejs') }) app.post('/register', checkNotAuthenticated, async (req, res) => { try { const hashedPassword = await bcrypt.hash(req.body.password, 10) const users = new User({ username: req.body.username, email: req.body.email, password: hashedPassword, role : 'basic' }) const newUser = await users.save() res.redirect('/login') } catch { res.redirect('/register') } }) app.listen(process.env.PORT || 3000)
解决方案
1. 修复查询函数的异步执行问题
Mongoose的find()方法返回的是Query对象,必须通过await执行才能拿到实际数据。同时,查询单个用户应该用findOne()或findById(),而非返回数组的find()。修改server.js中的initializePassport调用:
initializePassport(passport, async email => await User.findOne({ email: email }), async id => await User.findById(id) )
2. 调整passport-config.js中的认证逻辑
因为现在getUserByEmail返回的是单个用户文档而非数组,所以要修改认证和序列化逻辑:
async function initialize(passport, getUserByEmail, getUserById){ // 修正函数名拼写:athenticateUser → authenticateUser const authenticateUser = async (email, password, done) => { const user = await getUserByEmail(email) if (!user) { // 直接判断用户是否存在 return done(null, false, { message: '没有该邮箱对应的用户' }) } try { if (await bcrypt.compare(password, user.password)) { return done(null, user) // 直接传入单个用户对象 } else { return done(null, false, { message: '密码错误' }) } } catch (err) { return done(err) } } passport.use(new LocalStrategy({ usernameField: 'email'}, authenticateUser)) // 序列化时直接取user.id(Mongoose虚拟字段,对应_id的字符串形式) passport.serializeUser((user , done) => done(null, user.id)) // 反序列化时等待查询结果 passport.deserializeUser(async (id,done) => { const user = await getUserById(id) done(null, user) }) } module.exports = initialize
3. 额外优化点
- 修正
passport-config.js中的函数名拼写错误:athenticateUser改为authenticateUser - 登录成功后的
console.log(req.user)应该放在受保护路由中,比如/路由里,因为passport.authenticate的回调在重定向后不会执行:app.get('/', checkAuthenticated, (req, res) => { console.log(req.user) // 在这里打印用户信息 res.render('login') })
内容的提问来源于stack exchange,提问作者Davik Zetus
相关产品推荐
相关产品推荐

