LocalStrategy参数来源及异步函数变量跨函数访问的技术问题咨询
解答你的两个核心问题
1. LocalStrategy的email和password参数从哪来?
Passport的LocalStrategy会自动从你的登录请求中提取对应字段的值,具体逻辑是:
- 你在配置LocalStrategy时设置了
usernameField: 'email',所以Passport会去请求体(req.body)里找名为email的字段; passwordField: 'password'同理,会提取req.body里的password字段。
这些字段通常来自用户提交的登录表单(POST请求),或者前端发送的JSON请求体,只要键名匹配就能被Passport正确提取。
2. 为什么user_copy是undefined?怎么正确获取用户数据?
你遇到的问题是时序错误+全局变量滥用:
passport.serializeUser和passport.deserializeUser是在你调用initialize函数时就执行的,而authenticateUser只有当用户发起登录请求时才会运行,所以此时user_copy还没被赋值,自然是undefined;- 就算能赋值,用全局变量
user_copy也会有并发问题——多个用户同时登录时,这个变量会被覆盖,导致用户身份混乱。
正确的做法:利用Passport的回调传递机制
Passport的设计逻辑是:在authenticateUser验证通过后,把用户对象传给done函数,Passport会自动把这个用户对象传递给serializeUser;而deserializeUser则是根据序列化的id去重新查询用户,不需要依赖外部变量。
修正后的完整代码
首先是initialize函数的修正:
const LocalStrategy = require('passport-local').Strategy; function initialize(passport, getUserByEmail, getUserById) { const authenticateUser = async (email, password, done) => { // 简化await写法,不需要额外的then const userr = await getUserByEmail(email); if (!userr || userr.length === 0) { return done(null, false, { message: "No user with this email..." }); } try { // 这里写你的密码校验逻辑,比如对比哈希值 // 假设校验通过,把userr传给done return done(null, userr); } catch (e) { console.log(e); return done(e); } } passport.use(new LocalStrategy({ usernameField: 'email', passwordField: 'password' }, authenticateUser)); // serializeUser接收的是authenticateUser里传给done的user对象 passport.serializeUser((user, done) => { // 因为你的getUserByEmail返回的是数组,所以取第一个元素的_id done(null, user[0]._id); }); // deserializeUser接收的是序列化的id,用这个id去查用户 passport.deserializeUser(async (id, done) => { try { const user = await getUserById(id); done(null, user); } catch (e) { done(e); } }); } module.exports = initialize;
然后是调用initialize的代码修正(主要修复getUserById的错误):
initializePassport( passport, async function email(email) { await client.connect(); const db = client.db(dbName); const collection = db.collection(collectionName); // 简化写法,不需要then const found = await collection.find({ email: email }).toArray(); client.close(); return found; }, async function id(userId) { // 这里参数名改成userId,避免混淆 await client.connect(); const db = client.db(dbName); const collection = db.collection(collectionName); // 用传入的userId查询,而不是user.id,注意是_id(和序列化的字段对应) const found = await collection.find({ _id: userId }).toArray(); client.close(); return found; } );
关键逻辑梳理
- 用户发起登录请求,Passport提取
email和password传给authenticateUser; authenticateUser调用getUserByEmail查用户,验证密码后,把用户对象传给done(null, userr);- Passport把这个
userr传给serializeUser,序列化userr[0]._id到session; - 后续请求时,Passport从session取出id,传给
deserializeUser; deserializeUser用id调用getUserById查用户,把用户对象放到req.user里供后续使用。
这样就完全不需要依赖外部变量,也解决了时序问题,同时避免了并发风险。
内容的提问来源于stack exchange,提问作者Keklikobeko
相关产品推荐
相关产品推荐

