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

如何为Google Cloud App Engine Node API动态添加CORS站点

动态添加CORS允许域名解决方案

1. 替换静态CORS配置为动态校验逻辑

Express的cors中间件、Socket.IO的CORS配置均支持传入回调函数动态判断域名是否合法,无需硬编码固定数组:

Express CORS配置

// 从持久化存储拉取所有允许的域名列表,可自行实现DB/Redis查询逻辑
async function getAllowedOrigins() {
  // 示例:从MySQL查询所有生效的客户域名
  const [results] = await db.query('SELECT domain FROM customer_domains WHERE status = "active"');
  // 加上你本地开发的域名
  return [...results.map(item => item.domain), "http://localhost:8080", "http://localhost:8081"];
}

var app = express();
app.use(cors({
  credentials: true,
  origin: async (origin, callback) => {
    try {
      // 允许无origin的请求(如Postman调试),不需要可删除该判断
      if (!origin) return callback(null, true);
      const allowedList = await getAllowedOrigins();
      callback(null, allowedList.includes(origin));
    } catch (err) {
      callback(err, false);
    }
  }
}))

Socket.IO CORS配置

const httpServer = createServer(app);
const io = new Server(httpServer, { 
  cors: {
    credentials: true,
    methods: ["GET"],
    origin: async (origin, callback) => {
      try {
        if (!origin) return callback(null, true);
        const allowedList = await getAllowedOrigins();
        callback(null, allowedList.includes(origin));
      } catch (err) {
        callback(err, false);
      }
    }
  }
});

2. 选择持久化存储维护域名列表

不要将允许列表存在内存中,服务重启/多实例部署时数据会丢失。推荐使用以下存储方案:

  • 如果你已经在使用MySQL/PostgreSQL/MongoDB等业务数据库:新增customer_domains表,存储字段包括域名、客户ID、授权生效时间、授权到期时间、状态等,可同步实现授权到期自动下线逻辑
  • 如果追求查询性能:使用Redis存储允许域名列表,查询速度更快,适合高并发场景

3. 实现域名管理API

提供一个仅你内部销售后台可调用的接口,客户付费后自动调用该接口添加域名即可:

// 管理员接口鉴权中间件,避免公开接口被恶意调用
const adminAuth = (req, res, next) => {
  const requestKey = req.headers['x-admin-api-key'];
  // 密钥存在环境变量中,不要硬编码
  if (requestKey !== process.env.ADMIN_API_SECRET) {
    return res.status(403).json({code: 403, msg: '无权访问'});
  }
  next();
}

// 添加客户域名接口
app.post('/api/admin/add-customer-domain', adminAuth, async (req, res) => {
  const { domain, customerId, expireTime } = req.body;
  // 校验域名格式,必须带http/https前缀
  const domainReg = /^https?:\/\/[a-zA-Z0-9-._~:/?#[\]@!$&'()*+,;=]+$/;
  if (!domainReg.test(domain)) {
    return res.status(400).json({code: 400, msg: '域名格式非法,需包含http/https前缀'});
  }
  // 检查域名是否已存在
  const exist = await checkDomainExist(domain);
  if (exist) {
    return res.status(400).json({code: 400, msg: '域名已在允许列表中'});
  }
  // 存入持久化存储
  await saveDomainToDB({domain, customerId, expireTime, status: 'active'});
  // 如果你加了本地缓存,这里要删除缓存,确保最新配置立即生效
  clearOriginCache();
  res.status(200).json({code: 200, msg: '域名添加成功'});
});

4. 可选优化

  • 加本地缓存:允许域名列表不会频繁变动,可将查询结果缓存5-10分钟,避免每次请求都查询数据库,添加/删除域名时主动清除缓存即可
  • 多实例部署适配:如果你的服务部署了多个实例,使用Redis做共享缓存,避免本地缓存不一致的问题
  • 授权自动回收:定时扫描customer_domains表,将到期的域名状态改为失效,无需手动处理

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.30 07:15:05