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

无第三方服务的PWA离线认证方案及Firefox缓存兼容问题

PWA离线认证与缓存兼容问题

我正在开发一款支持离线运行的Progressive Web App(PWA),目前已成功实现卡组(decks)和卡片(cards)的缓存离线访问,但在用户进入应用时的初始认证环节遇到了问题:应用会请求/getuser接口,若用户处于离线状态则无法完成认证。

我当前的解决方案是缓存/getuser请求,从缓存中获取认证信息,但不确定这是否为安全的行业级方案。此外,该策略在Chrome中运行正常,但在Firefox中不符合预期:当用户会话过期后,Firefox会将该状态缓存,即便用户在线且已登录,仍会返回缓存的过期数据,导致已登录用户被重定向到会话过期页面,尽管我采用的是NetworkFirst缓存策略。

使用vite-plugin-pwa库配置Service Worker的代码如下:

import { defineConfig, loadEnv } from 'vite'
import react from "@vitejs/plugin-react-swc";
import { VitePWA } from "vite-plugin-pwa";


export default defineConfig(({ command, mode }) => {
  // In vite.config.ts we can't access env variables using import.meta.env.VITE_API_URL we have to use following code to access it.
  const env = loadEnv(mode, process.cwd())
  const API_URL = `${env.VITE_API_URL}`

  return {
    plugins: [react(), VitePWA({

      registerType: 'autoUpdate',
      devOptions: {
        enabled: true
      },
      injectRegister: 'auto',
      // add this to cache all the imports
      workbox: {
        globDirectory: "dist",
        globPatterns: ["**/*",],
        runtimeCaching: [
          {
            urlPattern: new RegExp(`${API_URL}/decks$`), //  your API endpoint
            handler: 'NetworkFirst', //  caching strategy 
            options: {
              cacheName: 'decks-cache',
              expiration: {
                maxEntries: 1, // number of entries to store
                maxAgeSeconds: 60 * 60 * 24 * 7, // cache expiration time
              },
              cacheableResponse: {
                statuses: [200]
              }
            },
          },
          {
            urlPattern: new RegExp(`${API_URL}/cards/.+`),
            handler: 'NetworkFirst', // Caching strategy for /cards/deck_id
            options: {
              cacheName: 'cards-cache',
              expiration: {
                maxEntries: 500,
                maxAgeSeconds: 60 * 60 * 24 * 7, // cache expiration time
              },
              cacheableResponse: {
                statuses: [200],
              },
            },
          },
          {
            urlPattern: new RegExp(`${API_URL}/decks-and-cards$`),
            handler: 'NetworkFirst',
            options: {
              cacheName: 'search-cache',
              expiration: {
                maxEntries: 500,
                maxAgeSeconds: 60 * 60 * 24 * 7, // cache expiration time
              },
              cacheableResponse: {
                statuses: [200],
              },
            },
          },
          {
            urlPattern: new RegExp(`${API_URL}/getuser$`),
            handler: 'NetworkFirst',
            options: {
              cacheName: 'auth-cache',
              expiration: {
                maxEntries: 500,
                maxAgeSeconds: 60 * 60 * 24 * 7, // cache expiration time
              },
              cacheableResponse: {
                statuses: [200],
              },
            },
          },
        ]
      },
      // add this to cache all the
      // static assets in the public folder
      includeAssets: [
        "**/*",
      ],
      manifest: {
        ....
     }
    }),],
  }
})

问题

  1. 不依赖第三方服务的PWA离线认证,更安全的行业标准方案是什么?
  2. 缓存/getuser请求是否为推荐方案?
  3. 如何解决Firefox中的缓存兼容问题?

解答

1. 不依赖第三方服务的PWA离线认证标准方案

核心采用本地轻量身份存储+离线权限降级的模式,具体实施:

  • 服务器端使用HttpOnly、Secure的Cookie存储会话ID,前端仅在localStorage/sessionStorage中存储非敏感的用户元数据(如用户ID、昵称、基础权限标记),绝不存储密码、完整权限列表等敏感信息。
  • 离线状态下,通过本地存储的元数据快速完成身份识别,仅开放用户已缓存的私有数据访问权限,禁止所有需要服务器交互的操作(如修改数据、同步新内容)。
  • 在线状态下,优先调用/getuser接口校验会话有效性:若会话过期则清除本地存储并引导登录;若有效则更新本地存储的用户信息。
  • 给本地存储的身份元数据设置过期时间,超过时限后即使用户离线也要求重新登录,在线时自动刷新过期时间。

2. 是否推荐缓存/getuser请求?

不推荐直接缓存/getuser接口的响应,原因如下:

  • 安全风险:缓存的响应可能包含敏感用户信息,一旦本地缓存被窃取会导致信息泄露;且缓存状态无法实时同步服务器的会话过期/注销操作。
  • 逻辑冲突:会出现像Firefox中那样,缓存的过期状态覆盖在线有效会话,导致用户体验异常。
  • 替代方案:改为缓存轻量化的用户身份元数据,仅用于离线时快速识别用户并开放缓存数据访问,在线时始终优先请求服务器校验会话。

3. Firefox缓存兼容问题的解决办法

针对Firefox中NetworkFirst策略失效的问题,可从以下几点调整:

(1)优化缓存策略配置

修改/getuser的缓存规则,缩短缓存有效期、添加缓存校验逻辑,避免无效状态被长期缓存:

{
  urlPattern: new RegExp(`${API_URL}/getuser$`),
  handler: 'NetworkFirst',
  options: {
    cacheName: 'auth-cache',
    expiration: {
      maxEntries: 1, // 仅保留最新1条缓存,避免多状态冲突
      maxAgeSeconds: 60 * 15, // 缩短缓存有效期至15分钟
    },
    cacheableResponse: {
      statuses: [200], // 仅缓存成功认证的响应,不缓存未授权状态
    },
    fetchOptions: {
      credentials: 'include',
    },
    plugins: [
      {
        cacheWillUpdate: async ({ response }) => {
          // 校验响应是否为有效认证状态,仅此时缓存
          const data = await response.clone().json();
          return data.isAuthenticated ? response : null;
        },
      },
    ],
  },
}

(2)在线请求强制绕过缓存

前端调用/getuser接口时,添加cache: 'no-store'选项,确保在线请求直接访问服务器:

fetch('/api/getuser', {
  credentials: 'include',
  cache: 'no-store' // 强制不使用缓存
})

(3)主动清除认证缓存

当用户执行登录、注销操作时,主动调用API删除auth-cache中的缓存条目:

if ('caches' in window) {
  caches.delete('auth-cache').then(() => {
    console.log('认证缓存已清除');
  });
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.03 23:20:55