为何使用passport与express-session时需解码会话Cookie?
技术栈概述
- 客户端:Next.js App Router
- 服务端:Express + express-session(Redis存储)+ Passport
服务端核心配置代码
index.ts(主配置)
app.use(helmet()); app.use(limiter); app.use(cors({ credentials: true, origin: "http://localhost:3000" })); app.use(express.json()); app.use(express.urlencoded({ extended: true })); app.use( session({ store: redisStore, secret: process.env.REDIS_SECRET || "secret", resave: false, saveUninitialized: false, proxy: process.env.NODE_ENV === "development" ? false : true, name: "_session", cookie: { sameSite: process.env.NODE_ENV === "development" ? "none" : "none", secure: process.env.NODE_ENV === "development" ? false : true, maxAge: 1000 * 60 * 60 * 24 * 1, httpOnly: process.env.NODE_ENV === "development" ? false : true, path: "/", domain: process.env.NODE_ENV === "development" ? "localhost:3000" : "", }, }) ); app.use(passport.initialize()); app.use(passport.session());
authMiddleware.ts(认证中间件)
export const authUser = (req: Request, res: Response, next: NextFunction) => { if (req.isAuthenticated()) { next(); return; } return res.status(401).json({ success: false, message: "Unauthorized" }); };
Passport序列化/反序列化
passport.serializeUser<any, any>((user: User, done: any) => { const safeUser: SafeUser = { id: user.id, email: user.email, name: user.name, bio: user.bio, image: user.image, googleId: user.googleId, appleId: user.appleId, }; done(null, safeUser); }); // eslint-disable-next-line @typescript-eslint/no-explicit-any passport.deserializeUser<any, any>((user: User, done: any) => { done(null, user); });
路由示例
router.get('/search/:search/', authUser, async (req: Request, res: Response, next: NextFunction) => { const userData = req.user as SafeUser; const email = userData.email try { const categories = await findMatchedCategory(req.params.search, email) res.json(categories); } catch (error) { next(error); } })
问题现象
- Postman测试所有接口正常,
req.isAuthenticated()能正确返回true - Next.js中使用服务端动作或客户端fetch调用时出现认证失败:
- 服务端动作获取用户:必须手动对
_sessionCookie值执行decodeURIComponent才能解析出有效会话ID,否则无法从Redis获取会话export const getUser = async () => { const userCookie = cookies().get("_session"); const rawSession: any = userCookie?.value; const decodeRawSession = decodeURIComponent(rawSession); if (!rawSession) return []; const sessionId = cookieParser.signedCookie(decodeRawSession, String(process.env.REDIS_SECRET) || "secret"); const session = await redis.get(`shub:${sessionId}`); if (!session) return []; const user = (session as any).passport.user; console.log('user from action', user) return user; } - 客户端fetch请求:请求头已携带Cookie,但服务端未手动解码时,
authUser中间件中req.isAuthenticated()始终返回falseconst followedCategories = await fetch(`http://localhost:3005/categories/search/${search}`, { method: 'GET', credentials: 'include', headers: { 'Content-Type': 'application/json', }, }) const data = followedCategories ? await followedCategories.json() : [];
- 服务端动作获取用户:必须手动对
当前临时解决方案
在服务端添加中间件手动解码_session Cookie:
app.use((req, res, next) => { const { headers: { cookie } } = req; if (cookie) { const allCookies = cookie.split(';').map(c => { if (!c.startsWith('_session')) return c; return `_session=${decodeURIComponent(c.split('=')[1])}` }); req.headers.cookie = allCookies.join('; '); next(); } else { next(); } })
该方案可行,但属于临时hack。曾尝试在session配置的cookie中添加encode: (v) => v,但无效。
疑问
想知道出现该问题的根本原因,或者是我的配置中存在哪些错误?
内容的提问来源于stack exchange,提问作者yungdino
相关产品推荐
相关产品推荐

