身份验证与授权

此页面仅适用于拥有旧版 Maps APIs for Work 或 Maps API for Business 许可的客户。此页面不适用于拥有新版 Google Maps APIs Premium Plan2016 年 1 月上市发售)的客户。

客户端 ID 和签名

对于 Google Maps APIs for Work 客户,图像 API(Static Maps API 和 Street View Image API)接受通过客户端 ID 和唯一的签名进行身份验证。

客户端 ID 和签名

将图像 API 与 Google Maps APIs for Work 许可搭配使用时,除了标准参数外,还需要以下两个身份验证参数:

  • 您的客户端 ID。要获取 Google Maps APIs for Work 的特殊功能,您在获取任意 API 内容库或服务时必须提供客户端 ID。注册 Google Maps APIs for Work 时,您将从 Google Cloud Support Portal 收到此客户端 ID。所有客户端 ID 都以 gme- 前缀开头。以 client 参数值的形式传递客户端 ID。

  • 唯一的签名,使用您的私有加密密钥生成。以 signature 参数值的形式传递此签名。您可以在下面的数字签名部分中找到有关生成签名的详细信息。

Static Maps API:

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

Street View Image API:

    <img src="https://maps.googleapis.com/maps/api/streetview
      ?location=40.720032,-73.988354
      &size=400x400
      &fov=90&heading=235&pitch=10
      &client=YOUR_CLIENT_ID
      &signature=SIGNATURE">

您在请求中不得包含 key 参数。

适用于 Google Maps APIs for Work 客户的数字签名

Google Maps APIs for Work 客户对图像 API 的请求需要一个数字 signature,此签名使用在欢迎电子邮件中向您提供的私有加密密钥生成。

签署流程使用一种加密算法将网址与密钥结合起来。我们的服务器可以通过生成的唯一签名来验证,任何使用您的客户端 ID 生成请求的网站都获得了相应授权。签名在每个网址下也是唯一的,从而确保在不生成新签名的情况下将无法修改使用您的客户端 ID 的请求。

您的私有加密密钥

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

:此私有加密签名密钥与 Google API Console 发放的 API 密钥相同。

如果您丢失了自己的私有加密密钥,请登录 Google Cloud Support Portal 并点击 Maps:Manage Client ID,检索该密钥。

生成数字签名

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

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

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

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

    Street View Image API:https://maps.googleapis.com/maps/api/streetview?location=41.403609,2.174448&size=456x456&client=clientID

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

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

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

    Street View Image API:/maps/api/streetview?location=41.403609,2.174448&size=456x456&client=clientID

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

    注:改良版 URL Base64 将标准版 Base64 的 + 字符和 / 字符分别替换成 -_,从而使这些 Base64 签名不再需要接受 URL 编码处理。

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

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

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

    Street View Image API:https://maps.googleapis.com/maps/api/streetview?location=41.403609,2.174448&size=456x456&client=clientID&signature=base64signature

如需了解有关使用服务器端代码实现网址签名的示例,请参阅网址签名的示例代码

URL 签名示例代码

下文介绍了利用服务器端代码实现 URL 签名的方法。应始终在服务器端签署 URL,以避免向用户公开密钥。

Python

下例使用标准 Python 内容库来签署 URL。(下载该代码。)

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

import hashlib
import hmac
import base64
import 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, 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

if __name__ == "__main__":
  input_url = raw_input("URL to Sign: ")
  secret = raw_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;
  }
}

C#

下例使用默认 System.Security.Cryptography 内容库来签署 URL 请求。请注意,我们需要转换默认 Base64 编码,才能实现 URL 安全版本。(下载该代码。)

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));
    }
  }
}

解决身份验证问题

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

如需解决个别 URL 问题,您可以使用 URL 签名调试器。您可以利用它快速验证由您的应用生成的 URL 和签名。

Google Maps APIs for Work 客户也可以登录 Google Cloud Support Portal 并选择 Resources > Google Maps APIs for Work online tools > URL Signing Debugger for Web Service and Image APIs,对各个网址进行问题排查。