注意:Google Maps Platform 高级计划不再开放注册,也不再提供给新客户。
选择身份验证方法
如需使用 Google Maps Platform,您必须使用 API 密钥或客户端 ID 以及数字签名对请求进行身份验证。
您应根据您的许可选择身份验证方法:
在决定要使用哪一种身份验证方法时,请考虑以下因素:
- 使用 API 密钥进行身份验证(高级计划许可)
通过使用 API 密钥来对请求进行身份验证,您可以:- 在 Google Cloud Console 的 API 页面上管理您的所有 API。
- 在 Cloud Console 的 API 页面上访问实时使用情况数据和 30 天的历史使用情况数据。
- 向请求添加
channel
参数,以便查看更详细的使用情况报告。 - 在 Google Cloud Console 中查看使用情况报告(包含 30 天以上的数据)。
- 使用客户端 ID 和数字签名进行身份验证(高级计划许可或旧版许可)
通过使用客户端 ID 和数字签名来对请求进行身份验证,您可以:- 向请求添加
channel
参数,以便查看更详细的使用情况报告。 - 在 Cloud Console 中查看使用情况报告(包含 30 天以上的数据)。
- 为 Maps JavaScript API 使用地图分析工具。
- 向请求添加
如需了解详情,请参阅面向高级计划客户提供的报告。
使用 API 密钥进行身份验证
获取 API 密钥
API 密钥是唯一标识符,用于对与您的项目相关联的请求进行身份验证,以便您使用相关产品和执行结算。
如需获取 API 密钥,请执行以下操作:
-
在 Cloud Console 中,打开项目选择器页面,选择或创建要向其添加 API 密钥的 Google Cloud 项目。
注意:如需对计划中的功能拥有完整访问权限,高级计划客户应使用与其高级计划帐号相关联的项目。购买许可时,您会收到如下格式的高级计划资产名称:
gme-[company] > proj-[number] ([type])
。为确保您访问的是正确的项目,请通过 console.cloud.google.com/project/number
(将number
替换为您的项目编号)以项目所有者身份登录控制台。您可以在欢迎邮件中找到项目所有者。 -
依次转到 Google Maps Platform > 凭据页面。
-
在凭据页面上,依次点击创建凭据 > API 密钥。
已创建的 API 密钥对话框会显示您新创建的 API 密钥。 -
点击关闭。
新的 API 密钥位于凭据页面的 API 密钥下。
(请务必在实际使用之前限制 API 密钥。)
向您的请求添加 API 密钥
您必须在每个 Google Maps Platform 请求中都添加一个 API 密钥。在以下示例中,将 YOUR_API_KEY
替换为您的 API 密钥。
https://maps.googleapis.com/maps/api/timezone/json?location=39.6034810,-119.6822510×tamp=1331161200&key=YOUR_API_KEY
对于使用 API 密钥的请求,必须使用 HTTPS;对于使用客户端 ID 的请求,建议使用 HTTPS。对于在请求中包含用户位置等敏感用户数据的应用,也必须使用 HTTPS。
限制 API 密钥
通过限制 API 密钥,您可以确保只能使用您的 API 密钥发出已授权的请求,从而提高应用安全性。我们强烈建议您按照相关说明为您的 API 密钥设置限制。如需了解详情,请参阅 API 安全性最佳做法。
如需限制 API 密钥,请执行以下操作:
-
依次转到 Google Maps Platform > 凭据页面。
- 选择您要设置限制的 API 密钥。系统随即会显示 API 密钥属性页面。
- 在密钥限制下,设置以下限制:
- 应用限制:
- 若要接受来自您提供的一系列网络服务器 IP 地址的请求,请从应用限制列表中选择 IP 地址(网络服务器、Cron 作业等)。
- 指定一个 IPv4 或 IPv6 地址,或采用 CIDR 表示法的子网(例如 192.168.0.0/22)。如果您需要输入其他条目,在您添加完上一个条目后,系统会显示一个新的文本框。
- API 限制:
- 点击限制密钥。
- 从选择 API 下拉列表中选择 Google Maps Platform。如果其中未列出 Google Maps Platform,您需要启用它。
- 点击保存以完成更改。
使用客户端 ID 和数字签名进行身份验证
购买 Google Maps Platform 专业版方案许可后,您将收到 Google 发送的一封欢迎电子邮件,其中包含您的客户端 ID 和私有加密密钥(您可以用来生成唯一的数字签名)。
以下代码示例显示了 client
和 signature
参数,您必须向其传递客户端 ID 和唯一的数字签名。
https://maps.googleapis.com/maps/api/timezone/json ?location=39.6034810,-119.6822510 ×tamp=1331161200 &client=YOUR_CLIENT_ID &signature=SIGNATURE
-
将
YOUR_CLIENT_ID
替换为欢迎电子邮件中包含的客户端 ID。您的客户端 ID 以“gme-”字符开头。
-
将
SIGNATURE
替换为您的唯一数字签名(请参阅生成数字签名)。
注意:
-
在使用客户端 ID 和数字签名对 Google Maps Platform 请求进行身份验证时,您也可以使用
channel
参数,以便接收详细的使用情况报告。如需了解详情,请参阅高级计划报告概览。 -
如果您之前使用 API 密钥进行身份验证,现在要改用客户端 ID,则必须从请求中移除
key
参数。Google Maps API 网络服务将拒绝同时使用客户端 ID 和 API 密钥发出的请求。
生成数字签名
Google Maps Platform 高级计划客户发出的 Time Zone API 请求需要数字 signature
,您可以使用欢迎电子邮件中向您提供的私有加密密钥生成该数字签名(请参阅详细了解私有加密密钥)。
请按照以下步骤为您的请求生成数字签名。
-
构建不带签名的请求网址,务必添加您的
client
参数。请注意,所有非标准字符都需要进行网址编码:https://maps.googleapis.com/maps/api/timezone/json?location=39.6034810,-119.6822510×tamp=1331161200&client=clientID
注意:所有 Google 服务都要求使用 UTF-8 字符编码(其中隐式包含 ASCII)。如果您的应用使用其他字符集运行,请确保其使用 UTF-8 构建网址,并对它们进行正确的网址编码。
-
去除请求的域名部分,只留下路径和查询:
/maps/api/timezone/json?location=39.6034810,-119.6822510×tamp=1331161200&client=clientID
-
检索您的私钥(采用改良版网址 Base64 进行编码),并使用 HMAC-SHA1 算法对上面的网址进行签名。您可能需要将此密钥解码成其原始二进制格式。请注意,在大多数加密库中,生成的签名均为二进制格式。
注意:改良版网址 Base64 将标准版 Base64 的
+
和/
字符分别替换成-
和_
,这样,这些 Base64 签名就不再需要进行网址编码了。 -
利用改良版网址 Base64 对生成的二进制签名进行编码,将该签名转换成可在网址内传递的内容。
-
将此签名附加到网址的
signature
参数中:https://maps.googleapis.com/maps/api/timezone/json?location=39.6034810,-119.6822510×tamp=1331161200&client=clientID&signature=base64signature
注意:
- 我们的服务器可以通过唯一签名来验证,是否使用您的客户端 ID 生成请求的所有网站都获得了相应授权。每个网址的签名也都是唯一的,这样一来,无需生成新的签名即可确保别人无法修改使用您的客户端 ID 的请求。
- 尝试使用无效签名访问 Time Zone API 将导致
HTTP 403
(Forbidden) 错误。在您将应用转换为使用网址签名时,请务必测试您的签名,确保它们可以发起有效请求。您首先应测试原始网址是否有效,并测试您是否生成正确的签名。 - 如需查看展示如何使用服务器端代码实现网址签名的示例,请参阅网址签名示例代码。
若要立即对网址进行签名,请在下方输入您的网址和网址签名密钥。网址必须采用上面第 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 服务验证。
- 网址:
https://maps.googleapis.com/maps/api/geocode/json?address=New+York&client=clientID
- 私钥:
vNIXE0xscrmjlyV-12Nj_BvUPaw=
- 要进行签名的网址部分:
/maps/api/geocode/json?address=New+York&client=clientID
- 签名:
chaRF2hTJKOScPr-RQCEhZbSzIE=
- 完整的已签名网址:
https://maps.googleapis.com/maps/api/geocode/json?address=New+York&client=clientID&signature=chaRF2hTJKOScPr-RQCEhZbSzIE=
其他语言的示例
可在网址签名项目中查看涵盖更多语言的示例。
详细了解私有加密密钥
您的私有加密网址签名密钥将随您的客户端 ID 一起发放,是您与 Google 之间的“共享密钥”。此签名密钥仅属于您,并且是您的客户端 ID 独有的。因此,请妥善保管您的签名密钥。此密钥不应在任何请求内传递、存储在任何网站上或发布到任何公共论坛中。任何获得该签名密钥的人都能利用您的身份伪造请求。
注意:此私有加密签名密钥与 Google Cloud Console 发放的 API 密钥不同。
如果您的私有加密密钥丢失,请登录 Cloud Console,然后点击 Maps: Manage Client ID 以检索该密钥。
排查身份验证问题
如果您的请求格式不正确,或者提供的签名无效,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。