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

Node.js Express中Session/Cookie登录后返回按钮导航异常排查

问题描述

我用Node.js Express结合Session和Cookie实现基础登录系统,登录成功后重定向至/home页面。但从/home点击浏览器返回按钮时会回到根路由/;双击返回按钮则会回到/并触发登出操作。

面试时面试官指出一个错误:点击返回按钮时匹配多条路由,但我不理解该问题的成因,也不知道解决方法。

  • 预期行为:访问/home后点击返回按钮,应停留在首页
  • 当前问题:从/home双击返回按钮会回到/并登出
  • 补充说明:控制台无错误信息,面试官未提供更多细节

相关代码

const express = require('express');
const app = express();
const hbs = require('hbs');
const nocache = require('nocache');
const session = require('express-session');

app.use(express.static('public'));
app.set('view engine', 'hbs');
app.use(express.urlencoded({ extended: true }));
app.use(express.json());

app.use(session({
    secret: "xyz",
    saveUninitialized: false,
    resave: true,
    cookie: { secure: false }
}));

app.use(nocache());

const username = 'admin';
const password = 'admin@123';

app.get('/', (req, res) => {
    if (req.session.user) {
        res.render('home', { username: username });
    } else if (req.session.passwordwrong) {
        res.render('login', { msg: "invalid credentials " });
        req.session.passwordwrong = false;
    } else {
        res.render('login');
    }
});

app.post('/verify', (req, res) => {
    if (req.body.username === username && req.body.password === password) {
        req.session.user = req.body.username;
        res.redirect('/home');
    } else {
        req.session.passwordwrong = true;
        res.redirect('/');
    }
});

app.get('/home', (req, res) => {
    if (req.session.user) {
        res.render('home', { username: username });
    } else {
        if (req.session.passwordwrong) {
            req.session.passwordwrong = false;
            res.render('login', { msg: "invalid credentials " });
        } else {
            res.render('login');
        }
    }
});

app.get('/logout', (req, res) => {
    req.session.destroy();
    res.render('login', { msg: 'logged out' });
});

app.listen(3000, () => console.log('server running on port 3000'));
问题原因分析
  1. 路由逻辑重复
    /和/home路由都包含已登录用户渲染home页面的逻辑,导致两条路由都能返回相同内容——这就是面试官所说的“匹配多条路由”。当用户点击返回按钮时,浏览器历史栈的操作可能触发/路由,而该路由同样显示home页面,造成行为混淆。

  2. 浏览器历史栈冗余
    登录流程的历史栈为:GET /(登录页)→ POST /verify(表单提交)→ GET /home(重定向)。点击返回按钮时,浏览器会尝试以GET方式请求/verify,但代码未处理该请求,导致浏览器 fallback 到/路由,此时历史栈新增一条/记录;第二次返回则回到最初的登录页历史记录,若session未正确维持(或用户误判),就会出现“登出”的错觉。

  3. Session配置不合理
    resave: true会强制每次请求都保存session,即使没有修改,可能引发不必要的session状态波动,间接影响页面跳转逻辑。

修复方案

1. 拆分路由职责,消除逻辑重复

修改/路由,让其仅负责显示登录页,已登录用户访问/时直接重定向到/home,明确/和/home的分工:

app.get('/', (req, res) => {
    if (req.session.user) {
        res.redirect('/home');
    } else if (req.session.passwordwrong) {
        res.render('login', { msg: "invalid credentials " });
        req.session.passwordwrong = false;
    } else {
        res.render('login');
    }
});

同时简化/home路由的未登录处理逻辑,直接重定向到/:

app.get('/home', (req, res) => {
    if (req.session.user) {
        res.render('home', { username: username });
    } else {
        res.redirect('/');
    }
});

2. 处理GET /verify请求

针对浏览器返回时的GET请求,添加GET /verify路由处理,根据登录状态重定向:

app.get('/verify', (req, res) => {
    if (req.session.user) {
        res.redirect('/home');
    } else {
        res.redirect('/');
    }
});

3. 优化Session配置

将resave设为false,避免不必要的session保存操作:

app.use(session({
    secret: "xyz",
    saveUninitialized: false,
    resave: false,
    cookie: { secure: false }
}));
最终修复后的代码
const express = require('express');
const app = express();
const hbs = require('hbs');
const nocache = require('nocache');
const session = require('express-session');

app.use(express.static('public'));
app.set('view engine', 'hbs');
app.use(express.urlencoded({ extended: true }));
app.use(express.json());

app.use(session({
    secret: "xyz",
    saveUninitialized: false,
    resave: false,
    cookie: { secure: false }
}));

app.use(nocache());

const username = 'admin';
const password = 'admin@123';

app.get('/', (req, res) => {
    if (req.session.user) {
        res.redirect('/home');
    } else if (req.session.passwordwrong) {
        res.render('login', { msg: "invalid credentials " });
        req.session.passwordwrong = false;
    } else {
        res.render('login');
    }
});

app.post('/verify', (req, res) => {
    if (req.body.username === username && req.body.password === password) {
        req.session.user = req.body.username;
        res.redirect('/home');
    } else {
        req.session.passwordwrong = true;
        res.redirect('/');
    }
});

app.get('/verify', (req, res) => {
    if (req.session.user) {
        res.redirect('/home');
    } else {
        res.redirect('/');
    }
});

app.get('/home', (req, res) => {
    if (req.session.user) {
        res.render('home', { username: username });
    } else {
        res.redirect('/');
    }
});

app.get('/logout', (req, res) => {
    req.session.destroy();
    res.render('login', { msg: 'logged out' });
});

app.listen(3000, () => console.log('server running on port 3000'));

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.22 06:33:11