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

Chrome扩展declarativeNetRequest动态规则不更新,令牌过期致401问题

问题分析与解决方案

核心问题

首次运行时令牌可正常传递,但1小时access token过期后,新令牌无法更新到declarativeNetRequest规则中,导致API请求返回401错误。现有实现的主要问题:

  1. webRequest.onBeforeRequest拦截请求时异步刷新令牌的时机太晚——请求已进入处理流程,无法及时更新规则修改当前请求的请求头
  2. 缺少对令牌过期时间的主动监控,依赖请求触发刷新的方式无法覆盖所有场景
  3. 未确认UpdateIntercept函数是否正确调用declarativeNetRequest.updateDynamicRules完成规则更新

解决方案

方案1:主动监控令牌过期,提前刷新并更新规则

这是最优方案,避免请求时临时处理的延迟问题:

  1. 存储令牌与过期时间
    从JWT中解析过期时间,或利用认证响应中的expires_in字段计算,同时存储refresh_token用于刷新:

    // 解码JWT获取过期时间(毫秒级)
    function getTokenExpiry(token: string): number {
      try {
        const payload = JSON.parse(atob(token.split('.')[1]));
        return payload.exp * 1000;
      } catch (e) {
        return 0;
      }
    }
    
    // 获取令牌后初始化存储与刷新调度
    chrome.identity.launchWebAuthFlow(
      { url: azureTokenAuthUrl, interactive: isInteractive },
      async (redirectURL: string) => {
        if (!redirectURL) return;
        // 注意:Azure认证响应的参数在hash中,而非query
        const params = new URLSearchParams(redirectURL.split('#')[1]);
        const accessToken = params.get("access_token") || '';
        const refreshToken = params.get("refresh_token") || '';
    
        // 存储令牌到本地
        await chrome.storage.local.set({ accessToken, refreshToken });
        // 更新拦截规则
        await UpdateIntercept(accessToken);
        // 调度过期前自动刷新
        scheduleTokenRefresh(accessToken);
        
        callback(accessToken);
      }
    );
    
  2. 自动刷新令牌并更新规则
    在令牌过期前5分钟触发刷新,更新规则后继续调度下一次刷新:

    async function scheduleTokenRefresh(token: string) {
      const expiryTime = getTokenExpiry(token);
      const refreshTime = expiryTime - 5 * 60 * 1000; // 过期前5分钟刷新
      const now = Date.now();
    
      if (refreshTime > now) {
        setTimeout(async () => {
          const newToken = await refreshAccessToken();
          if (newToken) {
            await UpdateIntercept(newToken);
            scheduleTokenRefresh(newToken);
          } else {
            // 刷新失败,触发重新登录
            chrome.runtime.sendMessage({ message: "get_token" });
          }
        }, refreshTime - now);
      }
    }
    
    // 用refresh_token获取新access_token的实现
    async function refreshAccessToken(): Promise<string | null> {
      const { refreshToken } = await chrome.storage.local.get("refreshToken");
      if (!refreshToken) return null;
    
      try {
        const response = await fetch(azureRefreshTokenUrl, {
          method: 'POST',
          body: new URLSearchParams({
            grant_type: 'refresh_token',
            refresh_token: refreshToken,
            client_id: yourClientId
          })
        });
        const data = await response.json();
        await chrome.storage.local.set({ accessToken: data.access_token });
        return data.access_token;
      } catch (e) {
        console.error("刷新令牌失败", e);
        return null;
      }
    }
    
  3. 完善规则更新函数
    确保正确替换旧规则:

    async function UpdateIntercept(newToken: string) {
      const rules = GetInterceptRules(newToken);
      await chrome.declarativeNetRequest.updateDynamicRules({
        removeRuleIds: [1, 2],
        addRules: rules
      });
    }
    

方案2:用webRequest直接修改请求头(备选)

如果不想用declarativeNetRequest,可以直接在请求发送前修改头,需要添加webRequestBlocking权限:

async function modifyAuthHeader(details: chrome.webRequest.WebRequestHeadersDetails) {
  const { accessToken } = await chrome.storage.local.get("accessToken");
  if (accessToken) {
    // 移除旧的Authorization头,添加新的
    details.requestHeaders = details.requestHeaders?.filter(h => h.name.toLowerCase() !== 'authorization') || [];
    details.requestHeaders.push({
      name: 'Authorization',
      value: `Bearer ${accessToken}`
    });
    return { requestHeaders: details.requestHeaders };
  }
  chrome.runtime.sendMessage({ message: "get_token" });
  return { requestHeaders: details.requestHeaders };
}

chrome.webRequest.onBeforeSendHeaders.addListener(
  modifyAuthHeader,
  { urls: [liveApiUrl, testApiUrl], initiatorDomains: ["mail.google.com"] },
  ["requestHeaders", "blocking"]
);

Manifest V3权限调整

如果使用方案2,需要在权限中添加:

"permissions": [
  "webRequest",
  "webRequestBlocking",
  "declarativeNetRequest",
  "declarativeNetRequestWithHostAccess",
  "identity",
  "identity.email",
  "storage"
]

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.02 10:40:50