You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Apollo Client:避免过期令牌阻塞无授权请求的实现方案

解决Apollo Client令牌过期阻塞公开请求的方案

你的问题核心在于所有请求都强制带上了x-token头(哪怕是空值),后端可能会对这个头做校验,空令牌/过期令牌都会被判定为无效,进而阻塞公开数据的获取。要解决这个问题,我们需要让请求"按需携带令牌"——仅给需要授权的编辑类请求添加令牌头,公开查看类请求完全不携带这个头。

下面是两种实用的实现方式:

方式一:通过请求上下文(Context)标记是否需要授权

这是最灵活的方案,你可以在发起每个请求时明确指定是否需要授权:

1. 修改Apollo Client配置

修改request函数,仅当请求标记了requiresAuth: true时,才添加x-token头:

import { ApolloClient, InMemoryCache } from '@apollo/client';

const client = new ApolloClient({
  uri: "http://localhost:8000/graphql",
  request: (operation) => {
    // 从请求上下文获取是否需要授权的标记
    const { requiresAuth } = operation.getContext();
    
    if (requiresAuth) {
      const token = sessionStorage.getItem('jwtToken');
      operation.setContext({
        headers: {
          'x-token': token || '',
        },
      });
    }
    // 不需要授权的请求,不设置x-token头,避免后端校验
  },
  cache: new InMemoryCache(),
});

2. 发起请求时标记授权需求

  • 编辑类请求(需要授权):在useMutation或useQuery中传入context: { requiresAuth: true }
import { useMutation } from '@apollo/client';
import UPDATE_POST_MUTATION from './mutations/UpdatePost';

const [updatePost] = useMutation(UPDATE_POST_MUTATION, {
  context: { requiresAuth: true }, // 标记需要授权
  // 其他配置:variables、onCompleted等
});
  • 查看类请求(无需授权):不需要传入context参数,请求会自动不带x-token头
import { useQuery } from '@apollo/client';
import GET_POSTS_QUERY from './queries/GetPosts';

const { data, loading } = useQuery(GET_POSTS_QUERY);
// 这个请求不会携带x-token头,后端不会校验令牌

方式二:通过操作名称(OperationName)自动区分

如果你的请求命名有统一规则(比如编辑类操作以Auth_开头),可以通过operationName自动判断是否需要授权:

修改Apollo Client配置

const client = new ApolloClient({
  uri: "http://localhost:8000/graphql",
  request: (operation) => {
    // 检查操作名称是否属于需要授权的类型
    if (operation.operationName?.startsWith('Auth_')) {
      const token = sessionStorage.getItem('jwtToken');
      operation.setContext({
        headers: {
          'x-token': token || '',
        },
      });
    }
  },
  cache: new InMemoryCache(),
});

比如你的编辑mutation命名为Auth_UpdatePost,查看query命名为GetPosts,这样就能自动区分,无需手动标记上下文。

额外优化:拦截令牌过期错误

对于需要授权的请求,我们可以在错误链路中拦截令牌过期错误,做自动登出等处理,同时不影响公开请求:

import { ApolloClient, InMemoryCache, createHttpLink, from } from '@apollo/client';
import { onError } from '@apollo/client/link/error';

// 创建HTTP链路
const httpLink = createHttpLink({
  uri: "http://localhost:8000/graphql",
});

// 创建错误拦截链路
const errorLink = onError(({ graphQLErrors, operation }) => {
  const { requiresAuth } = operation.getContext();
  
  // 仅处理需要授权的请求的令牌过期错误
  if (graphQLErrors && requiresAuth) {
    const isTokenExpired = graphQLErrors.some(err => err.message.includes('Token expired'));
    if (isTokenExpired) {
      // 清除过期令牌
      sessionStorage.removeItem('jwtToken');
      // 跳转到登录页
      window.location.href = '/login';
    }
  }
  // 公开请求的错误不做特殊处理,正常返回
});

// 组合链路
const client = new ApolloClient({
  link: from([errorLink, httpLink]),
  cache: new InMemoryCache(),
  request: (operation) => {
    const { requiresAuth } = operation.getContext();
    if (requiresAuth) {
      const token = sessionStorage.getItem('jwtToken');
      operation.setContext({
        headers: {
          'x-token': token || '',
        },
      });
    }
  },
});

这样一来,公开请求不会携带令牌,也就不会被令牌过期错误阻塞;授权请求的令牌过期会被自动处理,不影响用户体验。

内容的提问来源于stack exchange,提问作者Kenzo

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.12 04:54:15