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

VueJS+Express架构下通过AccessToken调用受保护API的安全方案咨询

VueJS+Express架构下无用户授权的JWT API调用安全方案

核心思路

将Express后端作为认证代理与API网关,由后端安全存储Client ID/Secret并负责与认证服务器交互获取JWT;同时通过多重机制保护Express的代理端点,防止被任意调用。

具体实现步骤

1. Express后端的角色定位与凭证存储

  • 把认证所需的CLIENT_ID、CLIENT_SECRET存储在Express的环境变量中(Azure App Service可通过「应用程序设置」配置,完全隔离于代码之外,不会泄露)。
  • 由Express负责向认证服务器发起client_credentials模式的令牌请求(此模式专为服务到服务的无用户授权场景设计),并缓存获取到的JWT,避免重复请求。

2. 保护Express代理端点的安全机制

针对Azure部署的Express服务,通过以下组合方式防止端点被滥用:

  • Origin校验:Express检查请求的Origin头,仅允许你的SPA域名(如https://your-spa-domain.com)发起的请求,拦截跨域恶意调用。
  • SPA专属API密钥:给SPA分配一个轻量API密钥,存储在SPA的环境变量中(虽能被前端查到,但仅用于访问你的Express代理,而非认证服务器的敏感凭证)。前端调用代理接口时在请求头X-SPA-API-Key中携带此密钥,Express验证通过后方可放行。
  • IP白名单:在Azure App Service的「IP限制」中配置SPA部署的固定IP(如Azure Static Web Apps的出站IP),仅允许该IP段访问Express服务。
  • 短期令牌(可选):若API密钥仍有风险,可让Express给SPA发放有效期较短的无敏感令牌(如15分钟),SPA用此令牌调用代理接口,过期后自动重新获取。

3. JWT缓存与自动刷新逻辑

  • 使用内存缓存(如node-cache)或Azure Redis存储JWT,记录令牌的过期时间。
  • 每次代理请求目标API前,检查缓存中的JWT是否即将过期(比如提前5分钟),若过期则自动向认证服务器刷新令牌,确保请求始终携带有效JWT。

代码示例

Express代理接口实现

const express = require('express');
const axios = require('axios');
const NodeCache = require('node-cache');
const app = express();

// 从环境变量读取配置(Azure App Service中配置)
const AUTH_SERVER_TOKEN_URL = process.env.AUTH_SERVER_TOKEN_URL;
const CLIENT_ID = process.env.CLIENT_ID;
const CLIENT_SECRET = process.env.CLIENT_SECRET;
const SPA_VALID_API_KEY = process.env.SPA_VALID_API_KEY;
const ALLOWED_SPA_ORIGIN = process.env.ALLOWED_SPA_ORIGIN;

// JWT缓存,TTL设为令牌有效期减5分钟(示例为3550秒=59分10秒)
const jwtCache = new NodeCache({ stdTTL: 3550 });

// 请求校验中间件
const validateSpaRequest = (req, res, next) => {
  // 校验Origin
  const requestOrigin = req.get('Origin');
  if (requestOrigin !== ALLOWED_SPA_ORIGIN) {
    return res.status(403).json({ error: '非法请求来源' });
  }

  // 校验SPA API密钥
  const requestApiKey = req.get('X-SPA-API-Key');
  if (!requestApiKey || requestApiKey !== SPA_VALID_API_KEY) {
    return res.status(401).json({ error: '未授权访问' });
  }
  next();
};

// 获取/刷新JWT令牌
const getValidJwtToken = async () => {
  let token = jwtCache.get('auth_token');
  if (token) return token;

  // 调用认证服务器获取令牌(client_credentials模式)
  const tokenResponse = await axios.post(AUTH_SERVER_TOKEN_URL, new URLSearchParams({
    grant_type: 'client_credentials',
    client_id: CLIENT_ID,
    client_secret: CLIENT_SECRET
  }), {
    headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
  });

  token = tokenResponse.data.access_token;
  jwtCache.set('auth_token', token);
  return token;
};

// 代理目标API的示例接口
app.get('/proxy/target-api-resource', validateSpaRequest, async (req, res) => {
  try {
    const jwtToken = await getValidJwtToken();
    // 调用目标API
    const apiResponse = await axios.get('https://target-api-domain.com/protected-resource', {
      headers: { Authorization: `Bearer ${jwtToken}` }
    });
    res.json(apiResponse.data);
  } catch (error) {
    res.status(error.response?.status || 500).json({ error: error.response?.data?.message || '服务调用失败' });
  }
});

app.listen(process.env.PORT || 3000, () => {
  console.log('Express proxy server running');
});

Vue前端调用示例

// src/api/proxy.js
import axios from 'axios';

// 从Vue环境变量读取配置
const PROXY_BASE_URL = process.env.VUE_APP_EXPRESS_PROXY_URL;
const SPA_API_KEY = process.env.VUE_APP_SPA_API_KEY;

// 调用代理接口获取目标API数据
export const fetchProtectedResource = async () => {
  const response = await axios.get(`${PROXY_BASE_URL}/proxy/target-api-resource`, {
    headers: {
      'X-SPA-API-Key': SPA_API_KEY
    }
  });
  return response.data;
};

Azure部署额外安全配置

  • 敏感配置隔离:所有敏感信息(如CLIENT_SECRET、SPA_VALID_API_KEY)均通过Azure App Service的「应用程序设置」配置,不写入代码或配置文件。
  • 强制HTTPS:在App Service的「TLS/SSL设置」中开启「HTTPS仅」,确保所有请求加密传输。
  • IP限制:在「网络」->「IP限制」中添加SPA部署的允许IP段,拒绝其他来源的请求。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.07 19:04:57