NestJS中如何在GraphQL解析器内设置会话密钥?Cookie认证问题求助
嘿,我之前在搭建Nest + GraphQL的认证系统时也踩过这个一模一样的坑!咱们一步步来排查和解决:
1. 确保Session中间件在GraphQL之前加载
express-session需要在GraphQL处理请求前就挂载到req对象上,否则GraphQL上下文里的req不会包含完整的session实例。检查你的main.ts,一定要让app.use(session(...))在app.listen()/app.init()之前执行,并且早于GraphQL模块的初始化。
正确的代码顺序示例:
// main.ts async function bootstrap() { const app = await NestFactory.create(AppModule); // 先注册session中间件 app.use( session({ store: new redisStore({ client: redis } as any), name: 'qid', secret: SESSION_SECRET, resave: false, saveUninitialized: false, cookie: { httpOnly: true, secure: !isDev, maxAge: 1000 * 60 * 60 * 24 * 7 * 365 } }) ); // 再启动服务/初始化其他模块 await app.listen(3000); } bootstrap();
2. 确保GraphQL上下文传递了req和res
默认情况下,GraphQL模块可能不会把res对象传入上下文,但express-session需要通过res来设置Set-Cookie响应头。在你的AppModule配置GraphQL时,一定要显式传递这两个对象:
// app.module.ts import { Module } from '@nestjs/common'; import { GraphQLModule } from '@nestjs/graphql'; import { join } from 'path'; @Module({ imports: [ GraphQLModule.forRoot({ autoSchemaFile: join(process.cwd(), 'src/schema.gql'), // 关键:把req和res传入上下文 context: ({ req, res }) => ({ req, res }), }), // 其他业务模块... ], }) export class AppModule {}
3. 手动触发Session保存(异步Resolver必备)
在异步的GraphQL resolver中,session的修改有时候不会自动触发持久化(尤其是修改嵌套属性时),这时候需要手动调用req.session.save(),确保修改被同步到Redis,并且响应头能正确发送Set-Cookie。
修改你的login resolver代码:
@Mutation('login') async login( @Args('email') email: string, @Args('password') password: string, @Context() ctx: any ) { console.log(ctx.req.session.id); // 能打印说明session已正常挂载 ctx.req.session.userId = 'something'; // 手动保存session,确保修改生效 await new Promise((resolve, reject) => { ctx.req.session.save((err) => { if (err) reject(err); else resolve(null); }); }); return await this.userService.login(email, password); }
4. 前端请求配置补充(浏览器端调用场景)
如果你的前端用Apollo Client之类的库发起请求,需要确保请求携带Cookie:
- 同域请求:设置
credentials: 'same-origin' - 跨域请求:设置
credentials: 'include',同时后端要配置CORS允许携带凭证(app.enableCors({ credentials: true, origin: '你的前端域名' }))
为什么中间件里能生效,Resolver里不行?
中间件是在请求生命周期早期同步执行的,session修改会在响应发送前自动完成保存;而GraphQL resolver是异步执行的,有时候请求结束后session的修改还没被处理,导致Set-Cookie头没被发送。另外,如果上下文没传res,session根本无法修改响应头,自然也不会生成Cookie。
内容的提问来源于stack exchange,提问作者Mika Void

