React结合Express Session与Apollo后端,如何校验用户认证状态?
问题分析与解决方法
1. 修正后端认证判断逻辑
你的isAuthenticated函数错误地从req.userId取值,但实际上登录后你是把userId存在req.session.userId里的,必须修改这个函数:
function isAuthenticated(req: any) { // 从session中获取userId,而非req本身 const userId: string = req?.session?.userId; return !!userId; }
2. 前端Apollo Client必须携带会话凭证
前端请求后端时需要自动带上session cookie,必须在Apollo Client的配置中开启withCredentials: true:
// 示例Apollo Client配置 import { ApolloClient, InMemoryCache, createHttpLink } from '@apollo/client'; const httpLink = createHttpLink({ uri: 'http://localhost:4000/graphql', // 开启携带凭证,让浏览器自动传递session cookie withCredentials: true, }); const client = new ApolloClient({ link: httpLink, cache: new InMemoryCache(), });
3. 修正CORS配置允许凭证传递
如果前端和后端跨域,CORS必须明确设置credentials: true,同时origin不能用通配符*,要指定具体的前端域名:
app.use( cors<cors.CorsRequest>({ origin: 'http://localhost:3000', // 替换为你的前端实际域名 credentials: true, // 允许跨域携带凭证 }), // ...其他中间件 );
4. (可选)登录后手动保存session
在登录resolver中,设置userId后手动调用req.session.save(),确保会话及时持久化到Redis,避免异步操作导致的session未保存问题:
signInUser: async (_, { user }, context) => { const { username, password } = user; const foundUser = await UserModel.findOne({ username }); if (foundUser) { const match = await bcrypt.compare(password, foundUser.password); if (match && context.req.session) { context.req.session.userId = foundUser.id; // 手动保存session到Redis await new Promise((resolve, reject) => { context.req.session.save((err: any) => { if (err) reject(err); else resolve(null); }); }); return { message: 'Signed in successfully!', status: 'success' }; } } // 补充登录失败的返回逻辑 return { message: 'Invalid username or password', status: 'error' }; },
验证流程
- 登录接口调用成功后,检查Redis中是否存在对应的session数据,确认userId已存入
- 调用
isAuthenticated查询时,后端能从req.session.userId获取到值,返回true - 前端通过
useAuthhook能正确拿到认证状态
内容的提问来源于stack exchange,提问作者Person
相关产品推荐
相关产品推荐

