使用express-session在Express中无法访问会话数据的问题排查
问题:Express-session会话数据在Nuxt.js Axios请求中无法获取
我在Node.js应用中使用express-session + connect-session-sequelize管控会话,将数据存储到MySQL数据库。登录成功后通过以下代码设置会话数据:
req.session.user = { id: user.id, email: user.email };
但后续Nuxt.js前端通过Axios发起API调用时,req.session.user始终为undefined,无法获取会话数据。
服务端配置代码
import express from 'express'; import cors from 'cors'; import { sequelize } from './Database/Models/index'; import cookieParser from 'cookie-parser'; import session from 'express-session'; import usersRoute from './Routers/usersRoute'; import checkAuthenticated from './Middlewares/checkAuthenticated'; const app = express(); app.use(cookieParser()) const SequelizeStore = require("connect-session-sequelize")(session.Store); var myStore = new SequelizeStore({ db: sequelize, checkExpirationInterval: 15 * 60 * 1000, expiration: 24 * 60 * 60 * 1000 }); app.use( session({ secret: 'secret', cookie: { secure: false }, saveUninitialized: false, resave: false, unset: 'destroy' }), ); myStore.sync(); app.use( cors({ origin: [ 'http://localhost:3000', 'https://localhost:3000' ], credentials: true, exposedHeaders: ['set-cookie'] }))
认证中间件代码
const checkAuthenticated = (req: any, res: any, next: any) => { if (req.session && req.session.user) { console.log(req.session.user) res.json({ loggedIn: true }); next(); } else { console.log(req.session) res.json({ loggedIn: false }); } }; export default checkAuthenticated;
登录接口代码
export const loginUser = async (req: any, res: any) => { try { const { error, value } = loginSchema.validate(req.body); if (error) { return res.status(400).json({ error: error.details[0].message }); } const user = await serviceLoginUser(value); req.session.user = { id: user.id, email: user.email, }; res.status(200).json({Login: true, user: req.session.user}); } catch (error: any) { const statusCode = error.code || 500; console.log(error.message) res.status(statusCode).json({ error: error.message }); } }
前端Axios调用代码
<script setup> import { ref } from 'vue'; import axios from 'axios'; axios.defaults.withCredentials = true; const email = ref(''); const password = ref(''); const showPassword = ref(false); const errors = ref(''); const togglePasswordVisibility = () => { showPassword.value = !showPassword.value; }; const handleSubmit = async () => { try { await axios.post("http://localhost:4000/users/signin", { email: email.value, password: password.value } , { withCredentials: true }); await navigateTo('/') } catch (error) { console.log(error) } } </script>
问题成因
- Session未关联SequelizeStore:创建了
myStore但未在session配置中指定store: myStore,导致会话默认存在内存中,未持久化到MySQL,跨请求易丢失。 - 中间件顺序错误:CORS中间件在session之后执行,OPTIONS预检请求会先经过session处理,引发跨域凭证传递问题。
- 缺少JSON解析中间件:服务端未配置
express.json(),可能导致登录接口无法正确解析请求体(若路由未单独配置)。 - Cookie配置不完整:跨域场景下未设置
sameSite属性,浏览器可能拒绝携带会话Cookie。 - 认证中间件逻辑错误:先返回响应再调用
next(),会导致后续路由无法执行,且触发重复响应的潜在问题。
修复方案
1. 修正服务端中间件配置
调整顺序并补全缺失配置:
import express from 'express'; import cors from 'cors'; import { sequelize } from './Database/Models/index'; import cookieParser from 'cookie-parser'; import session from 'express-session'; import usersRoute from './Routers/usersRoute'; import checkAuthenticated from './Middlewares/checkAuthenticated'; const app = express(); // 1. 解析JSON请求体 app.use(express.json()); // 2. 先配置CORS,处理跨域凭证 app.use( cors({ origin: [ 'http://localhost:3000', 'https://localhost:3000' ], credentials: true, exposedHeaders: ['set-cookie'] }) ); app.use(cookieParser()) const SequelizeStore = require("connect-session-sequelize")(session.Store); var myStore = new SequelizeStore({ db: sequelize, checkExpirationInterval: 15 * 60 * 1000, expiration: 24 * 60 * 60 * 1000 }); // 3. 配置session并关联Store app.use( session({ secret: 'your-strong-secret-key', // 建议用环境变量存储随机密钥 cookie: { secure: false, // 生产环境HTTPS下需设为true sameSite: 'lax', // 跨域场景确保Cookie被携带,HTTPS跨域可设为'none' maxAge: 24 * 60 * 60 * 1000 // 和会话过期时间保持一致 }, saveUninitialized: false, resave: false, unset: 'destroy', store: myStore // 关联SequelizeStore }), ); myStore.sync(); // 挂载路由 app.use('/users', usersRoute);
2. 修复认证中间件逻辑
避免重复发送响应:
const checkAuthenticated = (req: any, res: any, next: any) => { if (req.session && req.session.user) { console.log(req.session.user) next(); // 认证通过,放行至后续路由 } else { console.log(req.session) res.status(401).json({ loggedIn: false }); // 未认证返回401状态码 } }; export default checkAuthenticated;
3. 登录接口手动保存会话
确保会话数据立即写入数据库:
export const loginUser = async (req: any, res: any) => { try { const { error, value } = loginSchema.validate(req.body); if (error) { return res.status(400).json({ error: error.details[0].message }); } const user = await serviceLoginUser(value); req.session.user = { id: user.id, email: user.email, }; // 手动保存会话,确保数据持久化 await new Promise((resolve, reject) => { req.session.save((err) => { if (err) reject(err); else resolve(true); }); }); res.status(200).json({Login: true, user: req.session.user}); } catch (error: any) { const statusCode = error.code || 500; console.log(error.message) res.status(statusCode).json({ error: error.message }); } }
4. Nuxt.js Axios全局配置(可选)
在nuxt.config.ts中统一配置,避免组件内重复设置:
export default defineNuxtConfig({ modules: ['@nuxtjs/axios'], axios: { baseURL: 'http://localhost:4000', credentials: true } })
内容的提问来源于stack exchange,提问作者Abod Craft
相关产品推荐
相关产品推荐

