Heroku与Netlify部署后Express-Session无法存储会话值问题
跨域会话丢失问题排查与解决
本地环境(客户端localhost:3000、服务端localhost:3005)下,Steam登录后会话功能正常,能正确获取req.session.steamuser。但将服务端部署到Heroku、客户端部署到Netlify后,尽管在/api/auth/steam/return接口中已成功设置req.session.steamuser=req.user,访问/user接口时req.session.steamuser仍为null。
服务端代码(Server.js)
var express = require('express'); var passport = require('passport'); var session = require('express-session'); var passportSteam = require('passport-steam'); const cors = require("cors"); var SteamStrategy = passportSteam.Strategy; var app = express(); const corsOptions = { origin: ["https://stunning-bavarois-0eef55.netlify.app"], credentials: true, //access-control-allow-credentials:true methods: ["GET", "POST"], }; app.use(cors(corsOptions)); passport.serializeUser((user, done) => { done(null, user); }); passport.deserializeUser((user, done) => { done(null, user); }); passport.use(new SteamStrategy({ returnURL: 'https://temtestt.herokuapp.com/api/auth/steam/return', realm: 'https://temtestt.herokuapp.com/', apiKey: 'MY SECRET API KEY' }, function (identifier, profile, done) { process.nextTick(function () { profile.identifier = identifier; return done(null, profile); }); } )); app.use(session({ secret: 'db5910cc8b9dcec166fda1d2c34860b6f8cd932cea641ea39924ed18fe6fc863', resave: true, saveUninitialized: true, cookie: { SameSite:"none", maxAge: 3600000, secure:true } })) // Initiate Strategy app.use(passport.initialize()); app.use(passport.session()); app.get('/', (req, res) => { res.status(200); res.send("Welcome to root URL of Server"); }); app.get("/user", (req, res) => { if (req.session.steamuser) { res.status(200).send(req.session.steamuser) } else { res.send(false) } }) app.get('/api/auth/steam', passport.authenticate('steam', { failureRedirect: '/' }), function (req, res) { res.redirect('/') }); app.get('/api/auth/steam/return', passport.authenticate('steam', { failureRedirect: '/' }), function (req, res) { req.session.steamuser = req.user; res.redirect('https://stunning-bavarois-0eef55.netlify.app/') }); app.listen(process.env.PORT || 3005);
客户端代码(App.js)
import { useEffect, useState } from 'react'; import './App.css'; import axios from 'axios'; function App() { const [user,setUser]=useState(null); useEffect(()=>{ async function getUser(){ const data = await axios.get("https://temtestt.herokuapp.com/user",{withCredentials:true}); setUser(data.data); } getUser(); },[]); return ( <div className="App"> <h1>Hello</h1> {(user===false||user===null)?<><p>Please log in</p><a href="http://temtestt.herokuapp.com/api/auth/steam/return">Login</a></>:<p>{user.displayName}</p>} </div> ); } export default App;
问题原因与解决办法
1. 更换会话存储引擎
Heroku的临时文件系统会导致express-session默认的内存存储失效,应用重启或多实例部署时会话数据直接丢失。必须改用持久化存储,比如Redis或PostgreSQL:
const RedisStore = require('connect-redis').default; const redis = require('redis'); // 连接Heroku Redis服务 const client = redis.createClient({ url: process.env.REDIS_URL }); client.connect().catch(console.error); // 修改session配置 app.use(session({ secret: process.env.SESSION_SECRET || 'your-fallback-secret', resave: false, saveUninitialized: false, store: new RedisStore({ client }), cookie: { SameSite: "none", maxAge: 3600000, secure: process.env.NODE_ENV === 'production', domain: '.herokuapp.com' } }))
2. 修正Cookie配置细节
secure属性仅在生产环境开启:本地HTTP环境下设置secure: true会导致浏览器拒绝存储Cookie,生产HTTPS环境才需要启用。- 确保
SameSite: "none"与secure: true同时存在,否则浏览器会拦截跨域Cookie。
3. 修复客户端登录链接
客户端登录链接使用了HTTP协议,Heroku会强制重定向到HTTPS,可能打断会话流程。同时直接访问/return接口不符合Steam认证流程,应修改为:
<a href="https://temtestt.herokuapp.com/api/auth/steam">Login</a>
4. 简化会话获取逻辑
Passport会自动将认证用户信息存在req.user中,无需手动设置req.session.steamuser,直接用Passport提供的req.isAuthenticated()判断状态:
app.get("/user", (req, res) => { if (req.isAuthenticated()) { res.status(200).send(req.user) } else { res.send(false) } })
5. 改用环境变量管理敏感信息
将session secret、Steam API Key等敏感信息从代码中移除,通过Heroku环境变量配置,避免泄露且适配多环境需求。
内容的提问来源于stack exchange,提问作者Ole Dybedokken
相关产品推荐
相关产品推荐

