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'));
路由逻辑重复
/和/home路由都包含已登录用户渲染home页面的逻辑,导致两条路由都能返回相同内容——这就是面试官所说的“匹配多条路由”。当用户点击返回按钮时,浏览器历史栈的操作可能触发/路由,而该路由同样显示home页面,造成行为混淆。浏览器历史栈冗余
登录流程的历史栈为:GET /(登录页)→POST /verify(表单提交)→GET /home(重定向)。点击返回按钮时,浏览器会尝试以GET方式请求/verify,但代码未处理该请求,导致浏览器 fallback 到/路由,此时历史栈新增一条/记录;第二次返回则回到最初的登录页历史记录,若session未正确维持(或用户误判),就会出现“登出”的错觉。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

