使用来自.NET命令行应用程序的签名谷歌地图API地理编码请求

本文关键字:API 谷歌地图 请求 编码 NET 应用程序 命令行 | 更新日期: 2023-09-27 18:21:20

所以我正在编写一个应用程序,以便在导入记录时缓存地理编码数据。当我使用未签名的请求时,它运行良好,但当我尝试使用我公司的客户端ID和签名时,我似乎无法找出问题所在。我总是得到一个403禁忌。

这是我的URL生成器:

    private const string _googleUri = "http://maps.googleapis.com/maps/api/geocode/xml?address=";
    private const string _googleClientId = "XXXXXXXX";
    private const string _googleSignature = "XXXXXXXXXXXXXXXXXXXXXXXX";
//RESOLVED
    private static String GetGeocodeUri(string address)
    {
        ASCIIEncoding encoding = new ASCIIEncoding();
        string url = String.Format("{0}{1}&client={2}&sensor=false"
                                   , _googleUri
                                   , HttpUtility.UrlEncode(address)
                                   , _googleClientId);
        // converting key to bytes will throw an exception, need to replace '-' and '_' characters first.
        string usablePrivateKey = _googleSignature.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;
    } 

这是程序:

public static AddressClass GetResponseAddress(string address)
    {
        AddressClass GoogleAddress = new AddressClass();
        XmlDocument doc = new XmlDocument();
        String myUri = GetGeocodeUri(address);
        try
        {
            doc.Load(myUri);
            XmlNode root = doc.DocumentElement;
            if (root.SelectSingleNode("/GeocodeResponse/status").InnerText == "OK")
            {
                GoogleAddress.Latitude = Double.Parse(root.SelectSingleNode("/GeocodeResponse/result/geometry/location/lat").InnerText);
                GoogleAddress.Longitude = Double.Parse(root.SelectSingleNode("/GeocodeResponse/result/geometry/location/lat").InnerText);
            }
         }
         catch (Exception ex)
         {
            Console.WriteLine("Exception <" + ex.Message + ">");
         }           
        return GoogleAddress;
    }

现在,我对它不起作用的最初反应是,谷歌一定错过了referer域,因为它们必须注册。所以我用HttpWebRequest尝试了一下,并将referer设置为我的域,但仍然没有骰子。

//Not needed, Just an alternate method
public static AddressClass GetResponseAddress(string address)
    {
        AddressClass GoogleAddress = new AddressClass();
        WebClient client = new WebClient();
        XmlDocument doc = new XmlDocument();
        Uri myUri = new Uri(GetGeocodeUri(address));
        HttpWebRequest myRequest = (HttpWebRequest)WebRequest.Create(myUri);
        myRequest.Referer = "http://www.myDomain.com/";
        //I've even tried pretending to be Chrome
        //myRequest.UserAgent = "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/535.7 (KHTML, like Gecko) Chrome/16.0.912.75 Safari/535.7";
        try
        {
            doc.Load(myRequest.GetResponse().GetResponseStream());
            XmlNode root = doc.DocumentElement;
            if (root.SelectSingleNode("/GeocodeResponse/status").InnerText == "OK")
            {
                GoogleAddress.Latitude = Double.Parse(root.SelectSingleNode("/GeocodeResponse/result/geometry/location/lat").InnerText);
                GoogleAddress.Longitude = Double.Parse(root.SelectSingleNode("/GeocodeResponse/result/geometry/location/lat").InnerText);
            }
         }
         catch (Exception ex)
         {
              Console.WriteLine("Exception <" + ex.Message + ">");
         }
        return GoogleAddress;
    }

任何帮助都将不胜感激。

使用来自.NET命令行应用程序的签名谷歌地图API地理编码请求

const String gmeClientID = "gme-myClientId";
const String key = "myGoogleKey";
var urlRequest = String.Format("/maps/api/geocode/json?latlng={0},{1}&sensor=false&client={2}",Latitude,Longitude,gmeClientID);
HMACSHA1 myhmacsha1 = new HMACSHA1();
myhmacsha1.Key = Convert.FromBase64String(key); 
var hash = myhmacsha1.ComputeHash(Encoding.ASCII.GetBytes(urlRequest));
var url = String.Format("http://maps.googleapis.com{0}&signature={1}", urlRequest, Convert.ToBase64String(hash).Replace("+", "-").Replace("/", "_"));
var request = (HttpWebRequest)HttpWebRequest.Create(url);

URL编码有时是必要的(见下文),但还不够。事实上,你的问题是你没有签署你的请求。

_googleSignature常量中的值不是签名,而是私钥,这是错误的。您的私钥不应单独成为任何请求的一部分。

相反,您需要使用它为每个唯一的请求生成一个新的签名。请参阅Maps API for Business Authentication文档,其中还包括在Java中签名URL的示例:)

当使用您的Maps API for Business客户端id和您的私钥对Google Maps API Web Services的请求进行签名时,Referer标头和源IP地址完全无关;)

URL编码仅在address参数上是必需的,作为构建有效URL的一部分。您永远不应该对您的签名进行URL编码,因为通过使用修改后的Base64 for URL,签名已经是URL安全的了。

在将参数替换为查询字符串之前,您可能需要对参数进行正确的URL编码。如果您愿意导入System.Web程序集(而不使用客户端.NET配置文件),则可以使用HttpUtility.UrlEncode,也可以包含或从Microsoft的Web保护库中借用代码来执行此操作。

address = HttpUtility.UrlEncode(address); // better than Replace(" ", "+");
return String.Format("{0}{1}&client={2}&sensor=false&signature={3}",
                 _googleUri, address,
                 HttpUtility.UrlEncode(_googleClientId),
                 HttpUtility.UrlEncode(_googleSignature));

我想他们会检查请求来自的Ip是否与签名注册的域匹配。

你能试着从你的网络服务器发送请求吗?