Node服务器会话创建异常:认证与Cookie问题排查求助
现存问题
- 需要身份验证的路由无需登录或携带Cookie即可访问
- 登录后Cookie已设置,但登出后Cookie仍未清除
调试日志信息
1. 未设置Cookie时访问localhost:3000/
express-session no SID sent, generating session +0ms
2. 使用有效凭证请求localhost:3000/login/password
express-session no SID sent, generating session +0ms
express-session no SID sent, generating session +3m
express-session set-cookie connect.sid=s%3AuQgx27XnqODttfCECNGsaeJLOWZhk04e.qnHVj9HmUs2svxtb4mb6R9xpw3imbf0kUEhOiRlNXr4; Path=/; HttpOnly +105ms
express-session split response +9ms
express-session fetching uQgx27XnqODttfCECNGsaeJLOWZhk04e +439ms
express-session session found +8ms
express-session touching +5ms
express-session split response +2ms
express-session touched +10ms
3. 请求localhost:3000/logout
express-session fetching uQgx27XnqODttfCECNGsaeJLOWZhk04e +2m
express-session session found +4ms
express-session saving uQgx27XnqODttfCECNGsaeJLOWZhk04e +18ms
express-session set-cookie connect.sid=s%3ADEXRlWTn14GqFZU06YdLC-bemM8rn_ri.1v71XtDmg88F5brCD5yy3BsLvQ1UuGv%2FYS3vTeWD%2FUk; Path=/; HttpOnly +40ms
express-session split response +6ms
express-session fetching DEXRlWTn14GqFZU06YdLC-bemM8rn_ri +144ms
express-session session found +1ms
express-session touching +1ms
express-session split response +1ms
express-session touched +2ms
问题根源与修复方案
1. 路由未做身份验证拦截
你的代码里没有对需要保护的路由(比如/)添加身份验证校验逻辑,导致任何人都能直接访问。需要新增一个自定义中间件来检查用户是否已登录:
// 身份验证中间件 function ensureAuthenticated(req, res, next) { if (req.isAuthenticated()) { return next(); } res.redirect('/login'); // 未登录则跳转到登录页 }
将这个中间件应用到需要保护的路由上:
app.get("/", ensureAuthenticated, (req, res) => { res.json({ name: "Sourabh Roy", class: 9, section: "A", roll: 27, pass: "PASSED", }); });
2. 登出时未清除Cookie
默认的req.logout()只会销毁服务器端的会话数据,但不会主动清除客户端的Cookie。需要在登出逻辑中手动设置Cookie过期:
修改登出路由代码:
app.post("/logout", function (req, res, next) { req.logout(function (err) { if (err) { return next(err); } // 清除connect.sid会话Cookie res.clearCookie('connect.sid'); res.redirect("/"); }); });
额外说明
- 日志中
no SID sent是首次请求时客户端未携带会话Cookie,服务器生成新会话的正常现象; - 登出时出现新的
connect.sid是req.logout()默认会重新生成会话ID,添加clearCookie后客户端将不再携带旧Cookie; - 当前验证逻辑(
username == 1 && password == 1)仅适合测试,实际项目需从数据库查询用户并使用加密算法(如bcrypt)验证密码。
完整修复代码
const express = require("express"); const app = express(); const doten = require("dotenv"); const bodyParser = require("body-parser"); app.use(bodyParser.urlencoded({ extended: true })); const courses = [ { name: "aa", class: 5 }, { name: "bb", class: 7 }, { name: "cc", class: 8 }, { name: "dd", class: 4 }, ]; const user = [{}]; var passport = require("passport"); var LocalStrategy = require("passport-local"); var session = require("express-session"); passport.use( new LocalStrategy(function verify(username, password, cb) { if (username == 1 && password == 1) return cb(null, username); else return cb(null, false, { message: "Incorrect username or password." }); }) ); app.use( session({ secret: "keyboard cat", resave: false, saveUninitialized: false, cookie: { secure: false }, }) ); app.use(passport.authenticate("session")); passport.serializeUser(function (username, cb) { process.nextTick(function () { return cb(null, { username: username, }); }); }); passport.deserializeUser(function (user, cb) { process.nextTick(function () { return cb(null, user); }); }); // 身份验证中间件 function ensureAuthenticated(req, res, next) { if (req.isAuthenticated()) { return next(); } res.redirect('/login'); } app.post( "/login/password", passport.authenticate("local", { successRedirect: "/", failureRedirect: "/login", }) ); app.get("/", ensureAuthenticated, (req, res) => { res.json({ name: "Sourabh Roy", class: 9, section: "A", roll: 27, pass: "PASSED", }); }); app.get("/books/:name", (req, res) => { res.send({ ...req.params, ...req.query }); }); app.post("/signup", (req, res) => { let temp_user = req.body; console.log(req.body); user.push(temp_user); res.send(req.body); }); app.post("/logout", function (req, res, next) { req.logout(function (err) { if (err) { return next(err); } res.clearCookie('connect.sid'); res.redirect("/"); }); }); app.listen(3000, (err) => { if (err) console.log("hi"); else console.log("Server is up at port 3000"); });
内容的提问来源于stack exchange,提问作者sourabh roy

