天氣預報接口:SmartWeather API中key的計算方法


這個代碼大家都蠻感興趣,我在git開源了一個用於收集天氣信息的系統,基於python語言的,請大家參考:

https://github.com/BerlinSun/WeatherForecast

----------------------

最近申請到SmartWeatherAPI天氣預報接口的使用權限,開始着手我的實時天氣預報系統的開發,主要開發的版本使用的是Python腳本,成果將於近期以系列文章與大家見面。今天在這里我和大家探討一下SmartWeatherAPI中key的計算方法,並提供C++程序源碼供大家參考。

開發環境: Ubuntu + GCC4.7

零、 SmartWeather API的申請

SmartWeatherAPI(簡稱“SWA接口”)是中國氣象局面向網絡媒體、手機廠商、第三方氣象服務機構等用戶,通過web方式提供數據氣象服務的官方載體。是國內首個面向個人網站、開發愛好者和服務機構的氣象服務API數據開發接口(申請地址戳着里)。申請過程需要填寫一個表格,發送到官方郵箱,人工審核通過后會回復郵件,隨信提供appid和private_key等信息。

回復郵件部分內容如下(appid和private_key為私密信息,故用紅色橫線代替):

您好: 恭喜您的申請已通過審核,以下是為您分配的鑒權信息: appid:-------------- private_key:------------------------ 接口使用說明請參考《SmartWeatherAPI_Lite_WebAPI 版產品使用說明書》,區域列表:請見附件areaid_list.xlsx。 該鑒權信息僅限您個人或本公司使用,如有泄露我們將撤銷您的使用權限,必要時將追究相關責任。 最后,非常感謝您的參與。

一、 WebAPI接口說明書

接口說明說在上面所提到的API接口申請的網站可以下載到,下面就起說明書中幾個要點做以下說明:

1. 接口說明

請求方式: http get

接口組成: 由固定 URL 加 5 個不同的參數組成,完整URL需客戶端經過固定方式加密后使用

數據返回: json

完整URL: http://webapi.weather.com.cn/data/?areaid=""&type=""&date=""&appid=""&key=".urlencode($key)

輸入參數:

areaid: 區域id,審核通過后郵件中有個附件,提供的就是現有的所有區域的id號。

type: 數據類型(實況: observe, 指數: index, 常規預報: forecast3d)。

date: 客戶端日期,按照格式yyyyMMddHHmm獲取客戶端當前時間。

appid: 固定分配的型號標識,審核通過后郵件告知(傳遞參數時:截取 appid 的前 6 位; 生成公鑰時:取完整的 appid)。

key: 令牌,有公鑰(public_key)和私鑰(private_key)通過固定算法加密生成。

2. 加密方式

加密算法是今天博客的基礎,大家仔細閱讀。加密算法中涉及到的三個要素分別為:

  • private_key: 審核通過后,郵件中會提供。private_key僅負責與 public_key 共同合成 key 傳參,私鑰不可見,客戶端與服務端各存儲一份;
  • public_key: 不包含key在內的完整URL的其他部分(此處appid為完整appid);
  • key的算法: 說明書中提供的是php中的算法代碼,如下
  • 1 key = base64_encode(hash_hmac('sha1', $public_key, $private_key, TRUE));

    key加密后,通過 urlencode 對其編碼后傳參。

舉例說明,除去key部分的URL如下:

http://webapi.weather.com.cn/data/?areaid=101010100&type=index&date=201211281030&appid=cf2d61521456sads

私鑰private_key假設為: private_key

我們使用上述算法首先計算出加密后的key,得到key之后使用urlencode方法處理我們加密得到的key,結果為:

A%2Fp2QJ4R%2FD3FFCr6XwUCyNP56Y0%3D

則我們最后的輸入URL為:

http://webapi.weather.com.cn/data/?areaid=101010100&type=index&date=201211281030&appid=cf2d61&key=A%2Fp2QJ4R%2FD3FFCr6XwUCyNP56Y0%3D

3. 返回數據

輸出實例:

 1 {  2  "l" :  3  {  4  "l1" : "14",  5  "l2" : "46",  6  "l3" : "2",  7  "l4" : "6",  8  "l7" : "12:00"  9  } 10 }

其中:

l1: 表示當前溫度(攝氏度);

l2: 表示當前濕度(%);

l3: 表示當前風力(級);

l4: 表示當前風向編號(主頁有文獻可以查到具體的風向);

l7: 表示實況發布時間。

當前不同的type請求結果是大相徑庭的,具體的其他2類的我在這里不一一說明,需要了解的童鞋可以參考官方的文檔,這里順便列舉一個type=forecast3d的返回結果:

 1 {  2  "c" :  3  {  4  "c1" : "101270101",  5  "c10" : "1",  6  "c11" : "028",  7  "c12" : "610000",  8  "c13" : "104.071",  9  "c14" : "30.67", 10  "c15" : "507", 11  "c16" : "AZ9280", 12  "c17" : "+8", 13  "c2" : "chengdu", 14  "c3" : "成都", 15  "c4" : "chengdu", 16  "c5" : "成都", 17  "c6" : "sichuan", 18  "c7" : "四川", 19  "c8" : "china", 20  "c9" : "中國" 21  }, 22  "f" : 23  { 24  "f0" : "201401241100", 25  "f1" : 26  [ 27
28  { 29  "fa" : "01", 30  "fb" : "01", 31  "fc" : "16", 32  "fd" : "2", 33  "fe" : "4", 34  "ff" : "4", 35  "fg" : "0", 36  "fh" : "0", 37  "fi" : "07:59|18:32" 38  }, 39
40  { 41  "fa" : "01", 42  "fb" : "01", 43  "fc" : "16", 44  "fd" : "2", 45  "fe" : "4", 46  "ff" : "4", 47  "fg" : "0", 48  "fh" : "0", 49  "fi" : "07:58|18:33" 50  }, 51
52  { 53  "fa" : "01", 54  "fb" : "02", 55  "fc" : "14", 56  "fd" : "4", 57  "fe" : "4", 58  "ff" : "4", 59  "fg" : "0", 60  "fh" : "0", 61  "fi" : "07:58|18:34" 62  } 63  ] 64  } 65 }
View Code

二、 加密過程分析

在動手實現加密方法之前,我在想,首先我應該知道正確的加密結果撒,讓我看到正確的返回數據之后再去做這件事情不是更有意義。看官方提供的是php的方法,那我就先用php試一下唄,可我沒有安裝php阿,總不能因為做個小測試還整個php環境吧,麻煩!幸好有php online幫到我http://writecodeonline.com/php/

上面實例中的結果就是這么來的:

1 echo urlencode(base64_encode(hash_hmac('sha1', "http://webapi.weather.com.cn/data/?areaid=101010100&type=index&date=201211281030&appid=cf2d61521456sads", "private_key", TRUE)));

當然,實例中的URL是不會返回正確數據的,因為我們用的appid和private_key都是假造的,我在測試的過程使用的是我申請到的相關信息。

結果返回成功了,我們開始分析如何使用C++去實現這個加密過程吧!

1. 肢解加密過程

一口吃不成一個胖子,我們一步一步來,從最內層開始,從hash_hmac入手,說白了不就是hash過程了,這時候我想到了openssl,是的,就是他了!首先我們在php環境中求出我們這一步的結果再說:

1 echo hash_hmac('sha1', "http://webapi.weather.com.cn/data/?areaid=101010100&type=index&date=201211281030&appid=cf2d61521456sads", "private_key", TRUE);

結果如下:

1 �v@��=�*�_�����

使用openssl之前,讓我們分析以下php中的hash_hmac函數中的幾個參數,前三個估計一目了然,最后一個是什么意思呢?函數原型如下:

1 string hash_hmac( string $algo , string $data , string $key [, bool $raw_output = false ] )

最后一個參數raw_output解釋為: When set to TRUE, outputs raw binary data. FALSE outputs lowercase hexits.

好了,我們開始用openssl試試,先不着急用C++,先在bash下用command試試:

1 echo -n "http://webapi.weather.com.cn/data/?areaid=101010100&type=index&date=201211281030&appid=cf2d61521456sads" | openssl dgst -sha1 -binary -hmac "private_key"

看到這里的-binary參數沒有,這就是我之前為什么為專門分析hash_hmac最后一個參數的原因,因為我當時就在這里少了-binary參數,導致出來的結果老是對不上。好了,結果表明,和php的結果一致。

繼續肢解,開始第二步,base64_encode函數的分析,同樣, 先在php中來過:

1 echo base64_encode(hash_hmac('sha1', "http://webapi.weather.com.cn/data/?areaid=101010100&type=index&date=201211281030&appid=cf2d61521456sads", "private_key", TRUE));

結果如下:

1 A/p2QJ4R/D3FFCr6XwUCyNP56Y0=

我們來看看base64_encode函數的原型:

1 string base64_encode ( string $data )

這個函數,見名知意,就是對數據進行base64編碼。在linux下有現成的工具,即base64:

1 echo -n "http://webapi.weather.com.cn/data/?areaid=101010100&type=index&date=201211281030&appid=cf2d61521456sads" | openssl dgst -sha1 -binary -hmac "private_key" | base64

測試通過,我們開始最后一步,urlencode函數做什么,這個問題在之前一篇文章:http://www.cnblogs.com/berlin-sun/p/translateonline.html 中提到過,其實就是對url重新進行編碼,Returns a string in which all non-alphanumeric characters except -_. have been replaced with a percent (%) sign followed by two hex digits and spaces encoded as plus (+) signs.

2. 具體實現

hmac過程:

1 #include <openssl/hmac.h>
2  
3 unsigned char *digest;
4 digest = HMAC(EVP_sha1(), key, strlen(key), (unsigned char*)weather_api, strlen(weather_api), NULL, NULL);

base64過程:

 1 #include <openssl/bio.h>
 2 #include <openssl/buffer.h>
 3  
 4 char *base64(const unsigned char* input, int length)
 5 {
 6     BIO *bmem, *b64;
 7     BUF_MEM *bptr;
 8 
 9     b64 = BIO_new(BIO_f_base64());
10     bmem = BIO_new(BIO_s_mem());
11     b64 = BIO_push(b64, bmem);
12     BIO_write(b64, input, length);
13     BIO_flush(b64);
14     BIO_get_mem_ptr(b64, &bptr);
15 
16     char *buff = (char *)malloc(bptr->length);
17     memcpy(buff, bptr->data, bptr->length-1);
18 
19     buff[bptr->length-1] = 0;
20     BIO_free_all(b64);
21 
22     return buff;
23 }
24 
25 char *base64Digest = base64(digest, strlen((char *)digest));

urlencode過程:

1 #inlcude “curl/curl.h”
2 
3 CURL * curl;
4 curl = curl_easy_init();
5 
6 char *encode_key = curl_easy_escape(curl, base64Digest, 0);

目前,C++程序就完成了這些,結果已經成功返回,如下圖:

這就是我昨天晚上6點到7點半做的所有事情,然后得到女朋友的極大吐槽:一天到黑都在整,煩

編程改變世界,我是一個快樂的程序猿,謝謝大家閱讀!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM