Maps Static API:获取 API 密钥和签名

注意:Google Maps Platform 高级计划不再开放注册,也不再提供给新客户。

选择身份验证方法

如需使用 Google Maps Platform,您必须使用两个参数(API 密钥和数字签名,或者客户端 ID 和数字签名)对请求进行身份验证。为尽可能避免在代码中使用客户端 ID,请使用 API 密钥和数字签名(如果可以的话)。

您应根据您的许可选择身份验证方法:

  • 拥有高级计划许可的客户可以使用 API 密钥和数字签名,或者客户端 ID 和数字签名。
  • 拥有旧版许可的客户必须使用客户端 ID 和数字签名。

在决定要使用哪一种身份验证方法时,请考虑以下因素:

  • API 密钥(高级计划许可首选)- 通过使用 API 密钥对请求进行身份验证,您可以:
    • 在 Google Cloud Console 的 API 页面上管理您的所有 API。
    • Cloud Console 中访问实时使用情况数据和 30 天的历史使用情况数据。
    • 向请求添加 channel 参数,以便查看更详细的使用情况报告。
    • Google Cloud Console 中查看使用情况报告(包含 30 天以上的数据)。
    • 限制您的 API 密钥以防滥用。
  • 客户端 ID(高级计划许可或旧版许可)- 通过使用客户端 ID 对请求进行身份验证,您可以:
    • 向请求添加 channel 参数,以便查看更详细的使用情况报告。
    • Google Cloud Console 中查看使用情况报告(包含 30 天以上的数据)。
    • 为 Maps JavaScript API 使用地图分析工具。

使用 API 密钥和数字签名进行身份验证

创建 API 密钥

您必须至少有一个与您的项目相关联的 API 密钥。

要获取 API 密钥,请执行以下操作:

  1. 转到 Cloud Console
  2. 打开项目下拉菜单,选择在您购买高级计划时系统为您创建的项目。项目名称以 Google Maps API for Business 或 Google Maps for Work 或 Google Maps 开头。
  3. 注意:如需拥有对计划中的功能的完整访问权限,高级计划客户应使用与其高级计划帐号相关联的项目。购买许可时,您会收到如下格式的高级计划资产名称:gme-[company] > proj-[number] ([type])。为确保您访问的是正确的项目,请通过 console.cloud.google.com/project/number(将 number 替换为您的项目编号)以项目所有者身份登录控制台。您可以在欢迎邮件中找到项目所有者。

  4. 点击菜单按钮 ,然后选择 Google Maps Platform > 凭据
  5. 凭据页面上,依次点击创建凭据 > API 密钥
    已创建的 API 密钥对话框会显示您新创建的 API 密钥(一个加密字符串)。
  6. 点击关闭
    新的 API 密钥位于凭据页面的 API 密钥下。

向您的请求添加 API 密钥

您必须在每个 Google Maps Platform 请求中都添加一个 API 密钥。在以下示例中,将 YOUR_API_KEY 替换为您的 API 密钥。

https://maps.googleapis.com/maps/api/staticmap?center=40.714%2c%20-73.998&zoom=12&size=400x400&key=YOUR_API_KEY

对于使用 API 密钥的请求,必须使用 HTTPS;对于使用客户端 ID 的请求,建议使用 HTTPS。对于在请求中包含用户位置等敏感用户数据的应用,也必须使用 HTTPS。

限制 API 密钥

通过限制 API 密钥,您可以确保只能使用您的 API 密钥发出已授权的请求,从而提高应用安全性。我们强烈建议您按照相关说明为您的 API 密钥设置限制。如需了解详情,请参阅 API 密钥最佳做法

如需限制 API 密钥,请执行以下操作:

  1. 转到 Google Maps Platform > 凭据页面。

    转到“凭据”页面

  2. 选择您要设置限制的 API 密钥。系统随即会显示 API 密钥属性页面。
  3. 密钥限制下,设置以下限制:
    • 应用限制:
      1. 若要接受来自您提供的一系列网站的请求,请从应用限制列表中选择 HTTP 引荐来源网址(网站)
      2. 指定一个或多个引荐来源网址网站。例如,指定 *.google.com 表示接受所有以 google.com 结尾的网站,例如 https://developers.google.com

        注意:若要将 file:// 引荐来源网址添加到密钥限制中,需要使用特殊的表示法。首先将“file://”部分替换为“__file_url__”,然后再将其添加到密钥限制中。例如,“file:///path/to/”的格式应为“__file_url__//path/to/*”。启用 file:// 引荐来源网址之后,建议您定期检查使用情况,以确保与您的预期相符。

    • API 限制:
      1. 点击限制密钥
      2. 选择 API 下拉列表中选择 Google Maps Platform。如果其中未列出 Google Maps Platform,您需要启用它。
  4. 点击保存以完成更改。

生成数字签名

使用 key 参数(包含 API 密钥)向 Google Maps Platform 发出的请求还必须包括 signature 参数,其中包含您必须使用共享网址签名密钥生成的数字签名。您可以在 Google Cloud Console 上查看您的共享密钥。

签名流程使用一种加密算法将网址与您的共享密钥进行组合。我们的服务器可以通过生成的唯一签名来验证,使用您的 API 密钥生成请求的所有网站是否均获得了相应授权。

创建数字签名需要两个步骤:

第 1 步:获取您的网址签名密钥

您可在 Cloud Console 上查看您的加密网址签名密钥。该密钥也称为私钥,采用改良版网址 Base64 进行编码。该密钥在您与 Google 之间共享,是您的 API 密钥所独有的。请妥善保管您的网址签名密钥不要向其传递任何请求,也不要将其存储在任何网站上或发布到任何公共论坛中。任何获得该网址签名密钥的人都能利用您的身份伪造请求。

您在 Cloud Console 的此页面上获取网址签名密钥。重要提示:请选择购买高级计划时系统为您创建的项目。

此外,也可执行以下步骤来查看您的网址签名密钥:

  1. 转到 Cloud Console
  2. 点击项目下拉菜单,选择在您购买高级计划时系统为您创建的项目。项目名称以 Google Maps API for Business 或 Google Maps for Work 或 Google Maps 开头。
  3. 在“信息中心”页面的 API 列表中,点击 Google Maps Platform 的名称。系统随即会显示 API 详细信息,并打开概览标签页。
  4. 点击网址签名密钥标签页。

如需获取新的网址签名密钥,请点击重新生成密钥。旧密钥将在您生成新密钥后 24 小时失效。24 小时过后,包含旧密钥的请求将不再有效。

第 2 步:生成数字签名

如需为请求创建数字签名,请参阅使用 API 密钥生成数字签名

使用客户端 ID 和数字签名进行身份验证

了解客户端 ID 和加密密钥

购买 Google Maps Platform 高级计划许可后,您将收到 Google 发送的一封欢迎电子邮件,其中包含您的客户端 ID私有加密密钥

  • 您的客户端 ID 可用于访问 Google Maps Platform 高级计划的特殊功能。在以下示例中,将 YOUR_CLIENT_ID 替换为欢迎电子邮件中包含的客户端 ID。所有客户端 ID 都以 gme- 为前缀。

  • 您的私有加密密钥可用于生成唯一的数字签名。在以下代码示例中,将 SIGNATURE 替换为您的唯一数字签名。如需了解详情,请参阅使用客户端 ID 生成数字签名

    <img src="https://maps.googleapis.com/maps/api/staticmap
      ?center=-15.800513,-47.91378
      &zoom=11
      &size=300x300
      &client=YOUR_CLIENT_ID
      &signature=SIGNATURE">

如果您的客户端 ID 或私有加密密钥丢失,可以通过以下方式恢复:登录 Google Cloud Console,然后点击页面左侧的 Maps: Manage Client ID 链接。

报告的可选参数

在使用客户端 ID 进行 API 身份验证时,可使用以下可选参数:

  • channel 能够在报告中为不同的渠道单独分组,从而为报告提供更多详细信息。如需了解详情,请参阅高级计划报告概览

生成数字签名

如需为您的请求创建数字签名,请参阅使用客户端 ID 生成数字签名

数字签名

数字签名的运行方式

您可以使用 Google Cloud Console 中的加密网址签名密钥生成数字签名。该密钥也称为私钥,采用改良版网址 Base64 进行编码。该密钥在您与 Google 之间共享,是您的 API 密钥所独有的。

签名流程使用一种加密算法将网址与您的共享密钥进行组合。我们的服务器可以通过生成的唯一签名来验证使用您的 API 密钥生成请求的所有网站是否均获得了相应授权。

您可以使用 API 密钥客户端 ID 生成数字签名。

使用 API 密钥生成数字签名

请按照以下步骤创建与 API 密钥结合使用的数字签名:

  1. 构建不带签名的请求网址,务必在 key 参数中添加您的 API 密钥。请注意,您必须对所有非标准字符进行网址编码。例如:

    https://maps.googleapis.com/maps/api/staticmap?center=40.714%2c%20-73.998&zoom=12&size=400x400&key=YOUR_API_KEY

    注意:所有 Google 服务都要求使用 UTF-8 字符编码(其中隐式包含 ASCII)。如果您的应用使用其他字符集运行,请确保其使用 UTF-8 构建网址,并对它们进行正确的网址编码。

  2. 去除请求的域名部分,只留下路径和查询:

    /maps/api/staticmap?center=40.714%2c%20-73.998&zoom=12&size=400x400&key=YOUR_API_KEY

  3. 在 Cloud Console 中检索您的网址签名密钥(采用改良版网址 Base64 进行编码),并使用 HMAC-SHA1 算法对上面的网址进行签名(请参阅获取您的网址签名密钥)。

    您可能需要将密钥解码成其原始二进制格式。请注意,在大多数加密库中,生成的签名均为二进制格式。

    注意:改良版网址 Base64 将标准版 Base64 的 +/ 字符分别替换成 -_,这样,这些 Base64 签名就不再需要进行网址编码了。

  4. 利用改良版网址 Base64 对生成的二进制签名进行编码,将该签名转换成可在网址内传递的内容。

  5. 将生成的签名添加到请求网址中的 signature 参数中。例如:

    https://maps.googleapis.com/maps/api/staticmap?center=40.714%2c%20-73.998&zoom=12&size=400x400&key=YOUR_API_KEY&signature=BASE64_SIGNATURE

如需查看展示如何使用服务器端代码实现网址签名的示例,请参阅网址签名示例代码

若要立即对网址进行签名,请在下方输入您的网址和网址签名密钥。网址必须采用上面第 1 步中所述的格式,并进行网址编码。

使用客户端 ID 生成数字签名

使用 client 参数向 Static Maps API 发出的请求也需要数字 signature,您可以使用欢迎电子邮件中向您提供的私有加密密钥生成该数字签名。

签名流程使用一种加密算法将网址与密钥结合起来。我们的服务器可以通过生成的唯一签名来验证,是否使用您的客户端 ID 生成请求的所有网站都获得了相应授权。每个网址的签名也都是唯一的,这样一来,无需生成新的签名即可确保别人无法修改使用您的客户端 ID 的请求。

您的私有加密密钥

您的私有加密网址签名密钥将随您的客户端 ID 一起发放,是您与 Google 之间的“共享密钥”。此签名密钥仅属于您,并且是您的客户端 ID 独有的。因此,请妥善保管您的签名密钥。此密钥不应在任何请求内传递、存储在任何网站上或发布到任何公共论坛中。任何获得该签名密钥的人都能利用您的身份伪造请求。

注意:此私有加密签名密钥与 Cloud Console 发放的 API 密钥不同

如果您的私有加密密钥丢失,请登录 Cloud Console,然后点击 Maps: Manage Client ID 以检索该密钥。

使用客户端 ID 生成数字签名

尝试使用无效签名访问 Static Maps API 将导致 HTTP 403 (Forbidden) 错误。在您将应用转换为使用网址签名时,请务必测试您的签名,确保它们可以发起有效请求。您首先应测试原始网址是否有效,并测试您是否生成正确的签名。

请按照以下步骤为您的请求创建数字签名:

  1. 构建不带签名的请求网址,务必添加您的 client 参数。请注意,所有非标准字符都需要进行网址编码

    https://maps.googleapis.com/maps/api/staticmap?center=40.714%2c%20-73.998&zoom=12&size=400x400&client=clientID

    注意:所有 Google 服务都要求使用 UTF-8 字符编码(其中隐式包含 ASCII)。如果您的应用使用其他字符集运行,请确保其使用 UTF-8 构建网址,并对它们进行正确的网址编码。

  2. 去除请求的域名部分,只留下路径和查询:

    /maps/api/staticmap?center=40.714%2c%20-73.998&zoom=12&size=400x400&client=clientID

  3. 检索您的私钥(采用改良版网址 Base64 进行编码),并使用 HMAC-SHA1 算法对上面的网址进行签名。您可能需要将此密钥解码成其原始二进制格式。请注意,在大多数加密库中,生成的签名均为二进制格式。

    注意:改良版网址 Base64 将标准版 Base64 的 +/ 字符分别替换成 -_,这样,这些 Base64 签名就不再需要进行网址编码了。

  4. 利用改良版网址 Base64 对生成的二进制签名进行编码,将该签名转换成可在网址内传递的内容。

  5. 将此签名附加到网址的 signature 参数中:

    https://maps.googleapis.com/maps/api/staticmap?center=40.714%2c%20-73.998&zoom=12&size=400x400&client=clientID&signature=base64signature

如需查看展示如何使用服务器端代码实现网址签名的示例,请参阅网址签名示例代码

若要立即对网址进行签名,请在下方输入您的网址和网址签名密钥。网址必须采用上面第 1 步中所述的格式,并进行网址编码。

网址签名示例代码

以下各部分展示了使用服务器端代码实现网址签名的方法。应始终在服务器端对网址进行签名,以免将您的加密密钥暴露给用户。

Python

下例使用标准 Python 库对网址进行签名(下载代码)。

#!/usr/bin/python
# -*- coding: utf-8 -*-
""" Signs a URL using a URL signing secret """

import hashlib
import hmac
import base64
import urllib.parse as urlparse

def sign_url(input_url=None, secret=None):
    """ Sign a request URL with a URL signing secret.
      Usage:
      from urlsigner import sign_url
      signed_url = sign_url(input_url=my_url, secret=SECRET)
      Args:
      input_url - The URL to sign
      secret    - Your URL signing secret
      Returns:
      The signed request URL
  """

    if not input_url or not secret:
        raise Exception("Both input_url and secret are required")

    url = urlparse.urlparse(input_url)

    # We only need to sign the path+query part of the string
    url_to_sign = url.path + "?" + url.query

    # Decode the private key into its binary format
    # We need to decode the URL-encoded private key
    decoded_key = base64.urlsafe_b64decode(secret)

    # Create a signature using the private key and the URL-encoded
    # string using HMAC SHA1. This signature will be binary.
    signature = hmac.new(decoded_key, str.encode(url_to_sign), hashlib.sha1)

    # Encode the binary signature into base64 for use within a URL
    encoded_signature = base64.urlsafe_b64encode(signature.digest())

    original_url = url.scheme + "://" + url.netloc + url.path + "?" + url.query

    # Return signed URL
    return original_url + "&signature=" + encoded_signature.decode()

if __name__ == "__main__":
    input_url = input("URL to Sign: ")
    secret = input("URL signing secret: ")
    print("Signed URL: " + sign_url(input_url, secret))

Java

下例使用从 JDK 1.8 开始提供的 java.util.Base64 类 - 旧版本可能需要使用 Apache Commons 或类似工具(下载代码)。

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URISyntaxException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;  // JDK 1.8 only - older versions may need to use Apache Commons or similar.
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.net.URL;
import java.io.BufferedReader;
import java.io.InputStreamReader;

public class UrlSigner {

  // Note: Generally, you should store your private key someplace safe
  // and read them into your code

  private static String keyString = "YOUR_PRIVATE_KEY";

  // The URL shown in these examples is a static URL which should already
  // be URL-encoded. In practice, you will likely have code
  // which assembles your URL from user or web service input
  // and plugs those values into its parameters.
  private static String urlString = "YOUR_URL_TO_SIGN";

  // This variable stores the binary key, which is computed from the string (Base64) key
  private static byte[] key;

  public static void main(String[] args) throws IOException,
    InvalidKeyException, NoSuchAlgorithmException, URISyntaxException {

    BufferedReader input = new BufferedReader(new InputStreamReader(System.in));

    String inputUrl, inputKey = null;

    // For testing purposes, allow user input for the URL.
    // If no input is entered, use the static URL defined above.
    System.out.println("Enter the URL (must be URL-encoded) to sign: ");
    inputUrl = input.readLine();
    if (inputUrl.equals("")) {
      inputUrl = urlString;
    }

    // Convert the string to a URL so we can parse it
    URL url = new URL(inputUrl);

    // For testing purposes, allow user input for the private key.
    // If no input is entered, use the static key defined above.
    System.out.println("Enter the Private key to sign the URL: ");
    inputKey = input.readLine();
    if (inputKey.equals("")) {
      inputKey = keyString;
    }

    UrlSigner signer = new UrlSigner(inputKey);
    String request = signer.signRequest(url.getPath(),url.getQuery());

    System.out.println("Signed URL :" + url.getProtocol() + "://" + url.getHost() + request);
  }

  public UrlSigner(String keyString) throws IOException {
    // Convert the key from 'web safe' base 64 to binary
    keyString = keyString.replace('-', '+');
    keyString = keyString.replace('_', '/');
    System.out.println("Key: " + keyString);
    // Base64 is JDK 1.8 only - older versions may need to use Apache Commons or similar.
    this.key = Base64.getDecoder().decode(keyString);
  }

  public String signRequest(String path, String query) throws NoSuchAlgorithmException,
    InvalidKeyException, UnsupportedEncodingException, URISyntaxException {

    // Retrieve the proper URL components to sign
    String resource = path + '?' + query;

    // Get an HMAC-SHA1 signing key from the raw key bytes
    SecretKeySpec sha1Key = new SecretKeySpec(key, "HmacSHA1");

    // Get an HMAC-SHA1 Mac instance and initialize it with the HMAC-SHA1 key
    Mac mac = Mac.getInstance("HmacSHA1");
    mac.init(sha1Key);

    // compute the binary signature for the request
    byte[] sigBytes = mac.doFinal(resource.getBytes());

    // base 64 encode the binary signature
    // Base64 is JDK 1.8 only - older versions may need to use Apache Commons or similar.
    String signature = Base64.getEncoder().encodeToString(sigBytes);

    // convert the signature to 'web safe' base 64
    signature = signature.replace('+', '-');
    signature = signature.replace('/', '_');

    return resource + "&signature=" + signature;
  }
}

Node.js

下例使用原生节点模块对网址进行签名(下载代码)。

'use strict'

const crypto = require('crypto');
const url = require('url');

/**
 * Convert from 'web safe' base64 to true base64.
 *
 * @param  {string} safeEncodedString The code you want to translate
 *                                    from a web safe form.
 * @return {string}
 */
function removeWebSafe(safeEncodedString) {
  return safeEncodedString.replace(/-/g, '+').replace(/_/g, '/');
}

/**
 * Convert from true base64 to 'web safe' base64
 *
 * @param  {string} encodedString The code you want to translate to a
 *                                web safe form.
 * @return {string}
 */
function makeWebSafe(encodedString) {
  return encodedString.replace(/\+/g, '-').replace(/\//g, '_');
}

/**
 * Takes a base64 code and decodes it.
 *
 * @param  {string} code The encoded data.
 * @return {string}
 */
function decodeBase64Hash(code) {
  // "new Buffer(...)" is deprecated. Use Buffer.from if it exists.
  return Buffer.from ? Buffer.from(code, 'base64') : new Buffer(code, 'base64');
}

/**
 * Takes a key and signs the data with it.
 *
 * @param  {string} key  Your unique secret key.
 * @param  {string} data The url to sign.
 * @return {string}
 */
function encodeBase64Hash(key, data) {
  return crypto.createHmac('sha1', key).update(data).digest('base64');
}

/**
 * Sign a URL using a secret key.
 *
 * @param  {string} path   The url you want to sign.
 * @param  {string} secret Your unique secret key.
 * @return {string}
 */
function sign(path, secret) {
  const uri = url.parse(path);
  const safeSecret = decodeBase64Hash(removeWebSafe(secret));
  const hashedSignature = makeWebSafe(encodeBase64Hash(safeSecret, uri.path));
  return url.format(uri) + '&signature=' + hashedSignature;
}

C#

下例使用默认 System.Security.Cryptography 库对网址请求进行签名。请注意,我们需要转换默认 Base64 编码,才能实现网址安全版本(下载代码)。

using System;
using System.Collections.Generic;
using System.Security.Cryptography;
using System.Text;
using System.Text.RegularExpressions;
using System.Web;

namespace SignUrl {

  public struct GoogleSignedUrl {

    public static string Sign(string url, string keyString) {
      ASCIIEncoding encoding = new ASCIIEncoding();

      // converting key to bytes will throw an exception, need to replace '-' and '_' characters first.
      string usablePrivateKey = keyString.Replace("-", "+").Replace("_", "/");
      byte[] privateKeyBytes = Convert.FromBase64String(usablePrivateKey);

      Uri uri = new Uri(url);
      byte[] encodedPathAndQueryBytes = encoding.GetBytes(uri.LocalPath + uri.Query);

      // compute the hash
      HMACSHA1 algorithm = new HMACSHA1(privateKeyBytes);
      byte[] hash = algorithm.ComputeHash(encodedPathAndQueryBytes);

      // convert the bytes to string and make url-safe by replacing '+' and '/' characters
      string signature = Convert.ToBase64String(hash).Replace("+", "-").Replace("/", "_");

      // Add the signature to the existing URI.
      return uri.Scheme+"://"+uri.Host+uri.LocalPath + uri.Query +"&signature=" + signature;
    }
  }

  class Program {

    static void Main() {

      // Note: Generally, you should store your private key someplace safe
      // and read them into your code

      const string keyString = "YOUR_PRIVATE_KEY";

      // The URL shown in these examples is a static URL which should already
      // be URL-encoded. In practice, you will likely have code
      // which assembles your URL from user or web service input
      // and plugs those values into its parameters.
      const  string urlString = "YOUR_URL_TO_SIGN";

      string inputUrl = null;
      string inputKey = null;

      Console.WriteLine("Enter the URL (must be URL-encoded) to sign: ");
      inputUrl = Console.ReadLine();
      if (inputUrl.Length == 0) {
        inputUrl = urlString;
      }

      Console.WriteLine("Enter the Private key to sign the URL: ");
      inputKey = Console.ReadLine();
      if (inputKey.Length == 0) {
        inputKey = keyString;
      }

      Console.WriteLine(GoogleSignedUrl.Sign(inputUrl,inputKey));
    }
  }
}

其他语言的示例

可在网址签名项目中查看涵盖更多语言的示例。

排查身份验证问题

如果您的请求格式不正确,或者提供的签名无效,Google Maps Platform 会返回 HTTP 403 (Forbidden) 错误。

如需对各个网址进行问题排查,您可以使用网址签名调试程序。借助该调试程序,您可以快速验证网址和由您的应用生成的签名。

或者,高级计划客户也可以通过以下方式对各个网址进行问题排查:登录 Cloud Console,然后依次选择 Resources > Google Maps Platform Premium Plan online tools > URL Signing Debugger for Web Service and Image APIs