React+Express-GraphQL项目无法设置Refresh Token Cookie求助
我正在开发基于React.js的应用,后端采用Express-GraphQL服务器,通过JWT的Access Token和Refresh Token实现身份验证,客户端使用Apollo执行查询。目前遇到Refresh Token Cookie无法在客户端正确设置的问题,查阅相关文档和社区内容后仍未解决。两个应用均运行在localhost,但端口不同,服务器响应头中能看到Cookie字段,但客户端并未成功存储该Cookie。
后端中间件配置
app.use(cookieParser()) app.use(cors( { credentials: true, origin: "*", } ))
Apollo 链接配置
export const httpLink = createHttpLink({ uri: API_LINK + "/graphql", }); export const authLink = setContext((_, { headers }) => { const token = localStorage.getItem("accessToken"); if(token && (Date.now() >= jwtDecode(token).exp * 1000)){ return { headers: { ...headers, }} } if(!token){ return { headers: { ...headers, withCredentials: true }, }; } return { headers: { ...headers, authorization: token ? `Bearer ${token}` : "", }, }; }); export const getNewToken = async () => { try { const response = await fetch(API_LINK + "/graphql", { method: "POST", headers: { "Content-Type": "application/json", }, credentials: "include", body: JSON.stringify({ query: ` mutation { refreshToken{ type token } } ` })}); return response.json(); } catch (error) { console.log(error); } }; export const errorLink = onError( ({ graphQLErrors, networkError, operation, forward }) => { if(networkError?.statusCode === 401 || networkError?.statusCode === 500 ) { console.log(networkError) return fromPromise( getNewToken().then(({data}) =>{ console.log(data) localStorage.setItem("accessToken", data.refreshToken.token) return data.refreshToken.token }).catch((error) => { console.log(e) return; }) ) .filter((value) => Boolean(value)) .flatMap((accessToken) => { const oldHeaders = operation.getContext().headers; operation.setContext({ headers: { ...oldHeaders, authorization: `Bearer ${accessToken}`, }, }); return forward(operation); }); } if (graphQLErrors){ graphQLErrors.forEach(({message, locations, path}) =>{ if(message === "Помилка авторизації") { return fromPromise( getNewToken().then((data) =>{ console.log(data) if(!data?.refreshToken?.token){ return null } localStorage.setItem("accessToken", data?.refreshToken?.token) return data?.refreshToken?.token }).catch((error) => { console.log(error) return; }) ) .filter((value) => Boolean(value)) .flatMap((accessToken) => { const oldHeaders = operation.getContext().headers; operation.setContext({ headers: { ...oldHeaders, authorization: `Bearer ${accessToken}`, }, }); return forward(operation); }); } console.log( `[GraphQL error]: Message: ${message}, Location: ${locations}, Path: ${path}`, ) } ); } if (networkError) { console.log(`[Network error]: ${networkError}`); } } );
服务器端发送Cookie代码
res.set("Access-Control-Expose-Headers" ,"Set-Cookie") res.cookie('refreshToken', tokens.refreshToken, {maxAge: 30 * 24 * 60 * 60 * 1000 , httpOnly: true , secure: false})
ApolloClient 初始化代码
const client = new ApolloClient({ link: from([errorLink , authLink, httpLink]), cache: new InMemoryCache(), connectToDevTools: true, credentials: "include" })
解决方案
1. 修复CORS的origin配置
当开启credentials: true时,浏览器不允许使用"*"作为origin值,必须指定具体的客户端地址:
app.use(cors({ credentials: true, origin: "http://localhost:3000" // 替换为你的React应用实际端口 }))
若有多个客户端地址,可使用数组或函数动态校验来源。
2. 给httpLink添加credentials配置
ApolloClient的全局credentials配置可能不生效到具体链接,需给createHttpLink单独设置:
export const httpLink = createHttpLink({ uri: API_LINK + "/graphql", credentials: "include" // 新增该行 });
3. 移除authLink中无效的withCredentials设置
withCredentials并非请求头字段,不需要放在headers对象里,删除以下代码块:
if(!token){ return { headers: { ...headers, withCredentials: true // 该行无意义,直接删除 }, }; }
4. 显式配置Cookie的sameSite属性
Express默认将sameSite设为'Lax',跨端口场景下可显式指定避免浏览器默认行为冲突:
res.cookie('refreshToken', tokens.refreshToken, { maxAge: 30 * 24 * 60 * 60 * 1000, httpOnly: true, secure: process.env.NODE_ENV === 'production', // 生产环境开启HTTPS时设为true sameSite: 'Lax' // 显式声明,适配跨端口场景 })
5. 确认所有请求携带credentials
你的getNewToken函数已正确设置credentials: "include",确保所有与后端交互的请求都遵循该配置。
内容的提问来源于stack exchange,提问作者exxxamply
相关产品推荐
相关产品推荐

