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

使用Passport.js注册用户后无法持久化会话的求助

Passport.js 认证失效问题:注册后刷新/跳转页面需重新登录

注:目前仅使用json-server测试,未接入数据库。

注册路由功能正常,注册后能重定向到/posts且req.user存在新用户,但刷新页面或跳转后返回会被重定向到登录页,需重新登录。

注册路由代码

app.post('/signup', async (req, res) => {
  const { email, username, password } = req.body;

  if (!email || !username || !password) {
    req.flash('error', 'Missing credentials');
    res.redirect('/signup');
    return;
  }

  const response = await fetch('http://localhost:3000/users', {
    method: 'post',
    body: JSON.stringify(req.body),
    headers: { 'Content-Type': 'application/json' },
  });
  const data = await response.json();

  req.login(req.body, (err) => {
    if (err) {
      return next(err);
    }
    res.redirect('/posts');
  });
});

Posts页面路由代码

app.get('/posts/', async (req, res) => {
  if (req.isAuthenticated()) {
    console.log(req.user);
    const response = await fetch('http://localhost:3000/posts');
    const data = await response.json();
    const sortedData = data.toSorted(
      (a, b) => new Date(b.date) - new Date(a.date)
    );
    const users = await getUsers();

    const newData = sortedData.map((post) => ({
      ...post,
      author: users.find((user) => parseInt(user.id) === post.userId).username,
    }));

    res.render('posts.ejs', { posts: newData, user: req.user });
  } else {
    res.redirect('/signin');
  }
});

Passport配置代码

passport.use(
  new LocalStrategy(async function verify(username, password, cb) {
    const users = await getUsers();

    const matchedUser = users.find((user) => user.username === username);
    if (!matchedUser || matchedUser.password !== password) {
      return cb(null, false, { message: 'Incorrect username or password.' });
    }

    return cb(null, matchedUser);
  })
);

passport.serializeUser(function (user, cb) {
  cb(null, user);
});

passport.deserializeUser(function (user, cb) {
  cb(null, user);
});

Session与Cookie配置代码

app.use(
  session({
    secret: 'secretStringForNow',
    resave: false,
    saveUninitialized: false,
    cookie: {
      maxAge: 1000 * 60 * 60 * 24,
    },
  })
);
app.use(passport.initialize());
app.use(passport.session());

问题原因与解决方法

核心问题

  1. 注册时调用req.login(req.body, ...)传入的是前端提交的原始请求体,而非json-server返回的带id的完整用户对象。Passport序列化后存入session的对象缺少唯一标识,后续反序列化时无法与登录流程中获取的用户匹配,导致认证失效。
  2. 直接序列化整个用户对象,易出现session数据序列化/反序列化的兼容性问题,且存储效率低。
  3. saveUninitialized: false可能导致新用户的session未被持久化。

解决步骤

  1. 使用完整用户对象登录
    修改注册路由中req.login的参数,替换为json-server返回的data(带id的用户对象):

    // 原代码
    // req.login(req.body, (err) => {
    // 修改为
    req.login(data, (err) => {
      if (err) {
        return next(err);
      }
      res.redirect('/posts');
    });
    
  2. 优化序列化与反序列化逻辑
    仅存储用户唯一标识(id),反序列化时通过id重新从json-server获取用户:

    // 修改serializeUser
    passport.serializeUser(function (user, cb) {
      cb(null, user.id); // 只存储用户id
    });
    
    // 修改deserializeUser
    passport.deserializeUser(async function (id, cb) {
      const users = await getUsers();
      const user = users.find(u => u.id === id);
      cb(null, user); // 通过id获取完整用户对象
    });
    
  3. 调整session配置
    将saveUninitialized改为true,确保新用户的session被持久化:

    app.use(
      session({
        secret: 'secretStringForNow',
        resave: false,
        saveUninitialized: true,
        cookie: {
          maxAge: 1000 * 60 * 60 * 24,
        },
      })
    );
    

验证效果

修改后重新注册用户,刷新/posts页面或跳转其他页面再返回,应能保持登录状态,req.isAuthenticated()返回true。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.27 03:17:10