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

Nuxt 3:如何仅在服务器端存储access_token?

Nuxt3服务器端可修改存储access_token的可行方案

1. 模块级内存缓存(单实例部署)

  • 适合单服务器实例场景,利用Node.js模块级变量的持久特性存储token及过期时间,无需额外依赖。
  • 核心逻辑:在服务器端工具模块中维护token状态,每次请求前检查有效性,过期则重新从Auth0获取。

示例代码:
server/utils/auth0.ts

let cachedToken: string | null = null;
let tokenExpiresAt: number = 0;

export async function getAuth0Token() {
  // 提前10秒刷新,避免token在请求过程中过期
  if (cachedToken && Date.now() < tokenExpiresAt - 10000) {
    return cachedToken;
  }

  // 调用Auth0客户端凭证流获取token
  const response = await fetch('https://your-auth0-domain/oauth/token', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      client_id: useRuntimeConfig().private.auth0ClientId,
      client_secret: useRuntimeConfig().private.auth0ClientSecret,
      grant_type: 'client_credentials',
      audience: 'your-api-audience'
    })
  });

  const data = await response.json();
  cachedToken = data.access_token;
  tokenExpiresAt = Date.now() + (data.expires_in * 1000);

  return cachedToken;
}

在服务器API中调用:
server/api/fetch-data.ts

import { getAuth0Token } from '~/server/utils/auth0';

export default defineEventHandler(async (event) => {
  const token = await getAuth0Token();
  // 携带token请求后端REST服务
  const data = await fetch('https://your-backend-api/data', {
    headers: { Authorization: `Bearer ${token}` }
  });
  return data.json();
});

2. 服务器插件挂载缓存(符合Nuxt架构的单实例方案)

  • 将缓存逻辑封装为Nuxt服务器插件,挂载到Nitro应用实例上,更贴合Nuxt的扩展规范。

示例代码:
server/plugins/auth0-cache.ts

export default defineNitroPlugin((nitroApp) => {
  nitroApp.auth0Cache = {
    token: null as string | null,
    expiresAt: 0
  };
});

// 扩展TypeScript类型提示(可选)
declare module 'nitropack' {
  interface NitroApp {
    auth0Cache: {
      token: string | null;
      expiresAt: number;
    };
  }
}

修改工具函数使用插件缓存:
server/utils/auth0.ts

export async function getAuth0Token(nitroApp: NitroApp) {
  const { token, expiresAt } = nitroApp.auth0Cache;
  if (token && Date.now() < expiresAt - 10000) {
    return token;
  }

  const response = await fetch('https://your-auth0-domain/oauth/token', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      client_id: useRuntimeConfig().private.auth0ClientId,
      client_secret: useRuntimeConfig().private.auth0ClientSecret,
      grant_type: 'client_credentials',
      audience: 'your-api-audience'
    })
  });

  const data = await response.json();
  nitroApp.auth0Cache.token = data.access_token;
  nitroApp.auth0Cache.expiresAt = Date.now() + (data.expires_in * 1000);

  return data.access_token;
}

API中调用方式:
server/api/fetch-data.ts

import { getAuth0Token } from '~/server/utils/auth0';

export default defineEventHandler(async (event) => {
  const token = await getAuth0Token(event.context.nitroApp);
  const data = await fetch('https://your-backend-api/data', {
    headers: { Authorization: `Bearer ${token}` }
  });
  return data.json();
});

3. 外部缓存存储(多实例/集群部署)

  • 多实例或集群场景下,内存缓存无法跨实例共享,需使用Redis、Memcached等外部缓存服务,确保所有实例共用同一有效token。
  • 示例以Redis为例,先安装依赖:npm install ioredis

示例代码:
server/utils/redis.ts

import Redis from 'ioredis';

const redis = new Redis({
  host: useRuntimeConfig().private.redisHost,
  port: Number(useRuntimeConfig().private.redisPort),
  password: useRuntimeConfig().private.redisPassword // 按需配置
});

export default redis;

server/utils/auth0.ts

import redis from './redis';

export async function getAuth0Token() {
  // 从Redis读取缓存的token和过期时间
  const [cachedToken, expiresAtStr] = await redis.mget('auth0_token', 'auth0_token_expires_at');
  const expiresAt = Number(expiresAtStr);

  if (cachedToken && Date.now() < expiresAt - 10000) {
    return cachedToken;
  }

  // 从Auth0获取新token
  const response = await fetch('https://your-auth0-domain/oauth/token', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      client_id: useRuntimeConfig().private.auth0ClientId,
      client_secret: useRuntimeConfig().private.auth0ClientSecret,
      grant_type: 'client_credentials',
      audience: 'your-api-audience'
    })
  });

  const data = await response.json();
  const newExpiresAt = Date.now() + (data.expires_in * 1000);
  
  // 将token存入Redis,设置与token一致的过期时间
  await redis.multi()
    .set('auth0_token', data.access_token)
    .set('auth0_token_expires_at', newExpiresAt.toString())
    .expire('auth0_token', data.expires_in)
    .expire('auth0_token_expires_at', data.expires_in)
    .exec();

  return data.access_token;
}

API中的使用方式与单实例方案一致。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.19 15:27:41