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

Passport登录后Session无passport字段、req.user缺失问题排查

Passport本地登录Session丢失问题排查与解决

问题描述

使用Passport进行本地登录时,登录流程无报错,但session无法持久保存passport字段,请求对象中不存在req.user;登录后访问受保护的/welcome路由,返回「not authorization user」提示。

相关代码

初始化配置的initExpress函数

export default function initExpress(express, app) {
    app.use(express.json());
    app.use(express.urlencoded({ extended: true }));
    app.use(cors());
    setDbSessions(app, process.env.DB_URL); // 数据库与Session配置

  // 调试中间件
  app.use((req, res, next) => {
    console.log(" ----------------------------------- ");
    console.log('Session:', req.session);
    console.log(" ----------------------------------- ");
    next();
  });

    app.get("/welcome", isAuth, (req, res) => {
    console.log(req.session);
        res.send("<h1> welcome boss </h1>");
    });

    app.post("/pass-login", passport.authenticate("local"), (req, res) => {
        console.log(req.session);
        res.send("<h1> welcome from auth password login </h1>");
    });
}

数据库与Session配置函数

function setDbSessions(app, url) {
  mongoose.connect(url,{}).catch(error => console.log("App.js mongoose.connect error",error));
  let db = mongoose.connection;
  db.on('error', console.error);
  db.once('open', function(){
    console.log("App is connected to DB", db.name)
  });

  app.use(session({
    secret: process.env.SESSION_SECRET,
    resave: false,
    saveUninitialized: false,
    store: MongoStore.create({
      client: mongoose.connection.getClient() 
    }),
    cookie: {
      maxAge: 1000 * 60 * 60 * 24 
    },
  }));
    app.use(passport.initialize());
    app.use(passport.session());
    passport.use(localStratage);
}

Passport策略文件

import { Strategy } from "passport-local";
import { comparePassword } from "./bcrypt.mjs";
import { User } from "../dbScheme/UserScheme.mjs";
import passport from "passport";

const customFields = {
  username: 'username',
  password: 'password',
};

const checkValidFields = (username, password, done) => {
  User.findOne({ username: username })
    .then((user) => {
      if (!user) return done(null, false); 
      const match = comparePassword(password, user.password);
      if (match) {
        return done(null, user);
      }else {
        return done(null, false);
      }
    })
    .catch((err) => { 
      console.log(err);
      done(err); 
    });
};

passport.serializeUser((user, done) => {
    done(null, user.id);
});

passport.deserializeUser((userId, done) => {
  User.findById(userId)
    .then((user) => {
      done(null, user);
    })
    .catch(err => done(err))
});

const localStratage = new Strategy(customFields, checkValidFields);
export default localStratage;

身份验证中间件isAuth

export const isAuth = (req, res, done) => {
  console.log(req.session);
    if (req.isAuthenticated()) {
        done();
    } else {
        res.status(404).send("<h1> not authorzation user </h1>");
    }
};

问题现象

  • 登录/pass-login端点后输出的Session:
Session {
  cookie: {
    path: '/',
    _expires: 2024-07-14T11:27:14.100Z,
    originalMaxAge: 86400000,
    httpOnly: true
  },
  passport: { user: '66925ade46279d6d21a6491e' }
}
  • 访问/welcome时调试中间件输出的Session:
Session {
  cookie: {
    path: '/',
    _expires: 2024-07-14T11:36:32.934Z,
    originalMaxAge: 86400000,
    httpOnly: true
  }
}

原因分析

  1. MongoStore初始化时机错误:Session和Passport中间件在MongoDB连接建立前就已初始化,导致MongoStore无法正常连接数据库,Session无法持久化存储,每次请求都会生成新的Session,passport字段自然丢失。
  2. 密码校验异步处理错误:comparePassword(通常基于bcrypt,返回Promise)被同步赋值,导致match是Promise对象而非布尔值,可能引发逻辑错误(即使密码不匹配也会通过验证)。
  3. Session未手动保存:saveUninitialized: false配置下,Passport修改Session后可能未触发自动保存机制,导致Session变更未写入数据库。
  4. 身份验证中间件参数错误:isAuth中间件将第三个参数写为done,但Express中间件应使用next,导致认证通过后无法继续执行后续路由逻辑。

解决方案

1. 修正MongoStore初始化时机

将Session和Passport的初始化逻辑移至MongoDB连接成功后的回调中,确保数据库连接就绪:

function setDbSessions(app, url) {
  mongoose.connect(url,{}).catch(error => console.log("App.js mongoose.connect error",error));
  let db = mongoose.connection;
  db.on('error', console.error);
  db.once('open', function(){
    console.log("App is connected to DB", db.name);

    // 连接建立后再初始化Session和Passport
    app.use(session({
      secret: process.env.SESSION_SECRET,
      resave: false,
      saveUninitialized: false,
      store: MongoStore.create({
        client: db.getClient() 
      }),
      cookie: {
        maxAge: 1000 * 60 * 60 * 24 
      },
    }));
    app.use(passport.initialize());
    app.use(passport.session());
    passport.use(localStratage);
  });
}

2. 正确处理密码校验的异步逻辑

将checkValidFields改为异步函数,使用await处理密码比对:

const checkValidFields = async (username, password, done) => {
  try {
    const user = await User.findOne({ username: username });
    if (!user) return done(null, false);
    
    // 等待密码比对结果
    const match = await comparePassword(password, user.password);
    if (match) {
      done(null, user);
    } else {
      done(null, false);
    }
  } catch (err) {
    done(err);
  }
};

3. 登录成功后手动保存Session

在登录回调中调用req.session.save(),确保Session变更被持久化:

app.post("/pass-login", passport.authenticate("local"), (req, res) => {
  req.session.save((err) => {
    if (err) console.error("Session保存失败:", err);
    console.log(req.session);
    res.send("<h1> welcome from auth password login </h1>");
  });
});

4. 修正身份验证中间件参数

将isAuth的第三个参数改为next,并使用正确的HTTP状态码:

export const isAuth = (req, res, next) => {
  console.log(req.session);
  if (req.isAuthenticated()) {
    next(); // 调用next进入后续路由
  } else {
    res.status(401).send("<h1> not authorization user </h1>"); // 401表示未授权,而非404
  }
};

内容的提问来源于stack exchange,提问作者Yossef Sabry

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.20 23:07:05