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

Ionic Capacitor+React项目调用Google Places API如何隐藏密钥

解决Google Places API密钥在Web端暴露的问题

问题背景

我用Ionic Capacitor + React开发跨平台(原生+Web)应用,通过以下代码调用Google Places API获取图片,功能正常但网页检查时发现图片src里暴露了API密钥,怎么解决?

const fetchPhoto = async () => {
    if (photoName) {
      try {
        const maxHeightPx = 500;
        const maxWidthPx = 500;

        const url = `https://places.googleapis.com/v1/${photoName}/media?key=${GOOGLE_PLACES_API_KEY}&maxHeightPx=${maxHeightPx}&maxWidthPx=${maxWidthPx}`;
        const response = await CapacitorHttp.post({ url });
        setPhoto(response.url);
        setLoading(false);
      } catch (error) {
        setPhoto(images.thumbnail);
        setLoading(false);
        console.error("Error while fetching photo:", error);
      }
    }
  };

解决方案

1. 后端代理转发请求(最安全)

Web端直接请求Google API必然会暴露密钥,最彻底的办法是搭建自己的后端接口作为中间层:

  • 前端请求自有后端接口(比如/api/places/photo),携带photoName、maxHeightPx、maxWidthPx参数
  • 后端拿到参数后,用存储在服务器端的Google API密钥调用Places API,获取图片URL或直接返回图片数据
  • 前端从后端响应中拿到图片资源,密钥全程不会出现在前端

示例代码(前端调整):

const fetchPhoto = async () => {
  if (photoName) {
    try {
      const maxHeightPx = 500;
      const maxWidthPx = 500;
      // 请求自有后端接口
      const response = await CapacitorHttp.post({
        url: '/api/places/photo',
        data: { photoName, maxHeightPx, maxWidthPx }
      });
      setPhoto(response.data.url); // 后端返回的图片URL
      setLoading(false);
    } catch (error) {
      setPhoto(images.thumbnail);
      setLoading(false);
      console.error("Error while fetching photo:", error);
    }
  }
};

2. 严格限制Google API密钥的使用范围

如果暂时不想搭建后端,先给密钥加上严格限制,降低被滥用的风险:

  • 登录Google Cloud控制台,找到目标API密钥
  • 设置HTTP referrer限制:仅允许你的Web应用域名(比如https://your-app-domain.com/*)访问该密钥
  • 设置API服务限制:仅勾选Google Places API相关服务,禁止其他API使用该密钥
  • 即便密钥被暴露,攻击者也无法在其他域名或调用其他API时使用它

3. 区分原生与Web环境处理请求

利用Capacitor的Platform API,在原生端直接请求(原生环境密钥不会暴露),Web端走代理:

import { Platform } from '@capacitor/core';

const fetchPhoto = async () => {
  if (photoName) {
    try {
      const maxHeightPx = 500;
      const maxWidthPx = 500;
      let photoUrl;

      if (Platform.isWeb()) {
        // Web环境:请求后端代理
        const response = await CapacitorHttp.post({
          url: '/api/places/photo',
          data: { photoName, maxHeightPx, maxWidthPx }
        });
        photoUrl = response.data.url;
      } else {
        // 原生环境:直接调用Google API,密钥存在原生端(比如环境变量或安全存储)
        const url = `https://places.googleapis.com/v1/${photoName}/media?key=${GOOGLE_PLACES_API_KEY}&maxHeightPx=${maxHeightPx}&maxWidthPx=${maxWidthPx}`;
        const response = await CapacitorHttp.post({ url });
        photoUrl = response.url;
      }

      setPhoto(photoUrl);
      setLoading(false);
    } catch (error) {
      setPhoto(images.thumbnail);
      setLoading(false);
      console.error("Error while fetching photo:", error);
    }
  }
};

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.04 09:32:45