ホームページ >バックエンド開発 >PHPチュートリアル >PHP、Java、および C# は、アプリケーションと REST サーバー間の安全な通信を確保し、秘密キーの盗難やデータ改ざんなどの悪意のある攻撃を防止するために、URI パラメーター署名アルゴリズムを実装しています。
PHP、Java、および C# は、アプリケーションと REST サーバー間の安全な通信を確保し、秘密キーの盗難やデータ改ざんなどの悪意のある攻撃を防ぐための URI パラメーター署名アルゴリズムを実装しています
アプリケーションが HTTP POST または HTTP GET リクエストに基づいて Open API 呼び出しリクエストを送信するとき、アプリケーションと REST サーバー間の安全な通信を確保し、秘密キーの盗難やデータの改ざんなどの悪意のある攻撃を防ぐために、REST サーバーはパラメータ署名メカニズムを使用します。アプリケーションは Open API を呼び出す前に、すべてのリクエスト パラメータの MD5 署名を計算し、それをリクエスト パラメータに追加する必要があります。パラメータ名は「sign」です。 REST サーバーはリクエストを受信すると、署名を再計算し、その値がアプリケーションによって渡された署名パラメーター値と一致しているかどうかを判断して、現在の Open API 呼び出しリクエストが第三者によって偽造または改ざんされていないかどうかを判断します。
Open API を呼び出す前に、アプリケーションは OAuth2.0 サービスを通じてユーザーまたはプラットフォームから認可を取得する必要があります。認可を取得した後、次の 3 つの重要なパラメーターを取得します。
このうち、session_secret パラメータはパラメータの署名に必要な署名鍵です。これは、Facebook や Renren などのプラットフォームとは少し異なります。通常、これら 2 つのプラットフォームはパラメーター署名を行うときに 2 つの署名キーを使用します。
パラメーター署名の計算に参加するリクエスト パラメーターがそれぞれ "k1"、"k2"、および "k3" であると仮定します。値はそれぞれ「v1」、「v2」、「v3」です。パラメータ署名の計算方法は次のとおりです。
注: この時点では符号パラメータの値がまだ不明であり、計算する必要があるため、署名を計算するときにリクエスト パラメータに符号パラメータを含めないでください。
また、署名の計算時にはパラメータをurlencoded(「application/x-www-form-urlencoded」エンコード)する必要はありませんが、リクエスト送信時にはurlencoded処理が必要となります。多くの開発者にとって、間違いを犯しやすい最も一般的な問題です。
アプリケーションが uid 67411167 のユーザーの基本情報を取得する必要があり、アプリケーションが OAuth2 を通じてアクセス トークンを取得する前のプロセスにあるとします。 0 サービス 取得された session_key および session_secret パラメーターの値は次のとおりです:
Open API を呼び出すときのシステム時刻 (PHP では、date('Y-m-d H:i: s')) は「2011-06-21 17:18:09」です。REST サーバーが呼び出し結果を JSON 形式で返すことを期待します。つまり、パラメーター署名の計算に参加するリクエスト パラメーターのセットは次のとおりです。 >
<span style="color: #000000;">[ </span>"session_key" => "9XNNXe66zOlSassjSKD5gry9BiN61IUEi8IpJmjBwvU07RXP0J3c4GnhZR3GKhMHa1A="<span style="color: #000000;">, </span>"timestamp" => "2011-06-21 17:18:09"<span style="color: #000000;">, </span>"format" => "json"<span style="color: #000000;">, </span>"uid" => 67411167<span style="color: #000000;">]</span>
署名を計算する具体的なプロセスは次のとおりです:
<span style="color: #000000;"> [ </span>"session_key=9XNNXe66zOlSassjSKD5gry9BiN61IUEi8IpJmjBwvU07RXP0J3c4GnhZR3GKhMHa1A="<span style="color: #000000;">, </span>"timestamp=2011-06-21 17:18:09"<span style="color: #000000;">, </span>"format=json"<span style="color: #000000;">, </span>"uid=67411167"<span style="color: #000000;"> ]</span>
<span style="color: #000000;"> [ </span>"format=json"<span style="color: #000000;">, </span>"session_key=9XNNXe66zOlSassjSKD5gry9BiN61IUEi8IpJmjBwvU07RXP0J3c4GnhZR3GKhMHa1A="<span style="color: #000000;">, </span>"timestamp=2011-06-21 17:18:09"<span style="color: #000000;">, </span>"uid=67411167"<span style="color: #000000;"> ]</span>
format<span style="color: #339933;">=</span>jsonsession_key<span style="color: #339933;">=</span>9XNNXe66zOlSassjSKD5gry9BiN61IUEi8IpJmjBwvU07RXP0J3c4GnhZR3GKhMHa1A<span style="color: #339933;">=</span>timestamp<span style="color: #339933;">=</span><span style="color: #cc66cc;">2011</span><span style="color: #339933;">-</span><span style="color: #208080;">06</span><span style="color: #339933;">-</span><span style="color: #cc66cc;">21</span> <span style="color: #cc66cc;">17</span><span style="color: #339933;">:</span><span style="color: #cc66cc;">18</span><span style="color: #339933;">:</span>09uid<span style="color: #339933;">=</span><span style="color: #cc66cc;">67411167</span>
format<span style="color: #339933;">=</span>jsonsession_key<span style="color: #339933;">=</span>9XNNXe66zOlSassjSKD5gry9BiN61IUEi8IpJmjBwvU07RXP0J3c4GnhZR3GKhMHa1A<span style="color: #339933;">=</span>timestamp<span style="color: #339933;">=</span><span style="color: #cc66cc;">2011</span><span style="color: #339933;">-</span><span style="color: #208080;">06</span><span style="color: #339933;">-</span><span style="color: #cc66cc;">21</span> <span style="color: #cc66cc;">17</span><span style="color: #339933;">:</span><span style="color: #cc66cc;">18</span><span style="color: #339933;">:</span>09uid<span style="color: #339933;">=</span>6741116727e1be4fdcaa83d7f61c489994ff6ed6
接下来便可以通过HTTP POST方法或HTTP GET方法请求Open API的REST服务器,进行接口调用了,如:
GET /rest/2.0/passport/users/getInfo?session_key=9XNNXe66zOlSassjSKD5gry9BiN61IUEi8IpJmjBwvU07RXP0J3c4GnhZR3GKhMHa1A%3D×tamp=2011-06-21+17%3A18%3A09&format=json&uid=67411167&sign=d24dd357a95a2579c410b3a92495f009 HTTP/1.1<span style="color: #000000;">Host: openapi.baidu.comUser</span>-<span style="color: #000000;">Agent: Client of Baidu Open PlatformAccept: </span>*<span style="color: #008000;">/*</span><span style="color: #008000;">Accept-Encoding: gzip,deflateAccept-Charset: utf-8Connection: close或POST /rest/2.0/passport/users/getInfo HTTP/1.1Host: openapi.baidu.comUser-Agent: Client of Baidu Open PlatformAccept: </span><span style="color: #008000;">*/</span>*<span style="color: #000000;">Accept</span>-<span style="color: #000000;">Encoding: gzip,deflateAccept</span>-Charset: utf-8<span style="color: #000000;">Content</span>-Length: 179<span style="color: #000000;">Connection: close session_key</span>=9XNNXe66zOlSassjSKD5gry9BiN61IUEi8IpJmjBwvU07RXP0J3c4GnhZR3GKhMHa1A%3D×tamp=2011-06-21+17%3A18%3A09&format=json&uid=67411167&sign=d24dd357a95a2579c410b3a92495f009
获取签名的PHP代码实现方式如下所示:
<span style="color: #008000;">/*</span><span style="color: #008000;">* * 签名生成算法 * @param array $params API调用的请求参数集合的关联数组,不包含sign参数 * @param string $secret 签名的密钥即获取access token时返回的session secret * @return string 返回参数签名值 </span><span style="color: #008000;">*/</span> <span style="color: #0000ff;">function</span> getSignature(<span style="color: #800080;">$params</span>, <span style="color: #800080;">$secret</span><span style="color: #000000;">) { </span><span style="color: #800080;">$str</span> = ''; <span style="color: #008000;">//</span><span style="color: #008000;">待签名字符串 //先将参数以其参数名的字典序升序进行排序</span> <span style="color: #008080;">ksort</span>(<span style="color: #800080;">$params</span><span style="color: #000000;">); </span><span style="color: #008000;">//</span><span style="color: #008000;">遍历排序后的参数数组中的每一个key/value对</span> <span style="color: #0000ff;">foreach</span> (<span style="color: #800080;">$params</span> <span style="color: #0000ff;">as</span> <span style="color: #800080;">$k</span> => <span style="color: #800080;">$v</span><span style="color: #000000;">) { </span><span style="color: #008000;">//</span><span style="color: #008000;">为key/value对生成一个key=value格式的字符串,并拼接到待签名字符串后面</span> <span style="color: #800080;">$str</span> .= "<span style="color: #800080;">$k</span>=<span style="color: #800080;">$v</span>"<span style="color: #000000;">; } </span><span style="color: #008000;">//</span><span style="color: #008000;">将签名密钥拼接到签名字符串最后面</span> <span style="color: #800080;">$str</span> .= <span style="color: #800080;">$secret</span><span style="color: #000000;">; </span><span style="color: #008000;">//</span><span style="color: #008000;">通过md5算法为签名字符串生成一个md5签名,该签名就是我们要追加的sign参数值</span> <span style="color: #0000ff;">return</span> <span style="color: #008080;">md5</span>(<span style="color: #800080;">$str</span><span style="color: #000000;">); }</span>
调用示例:
<span style="color: #800080;">$uid</span> = 67411167<span style="color: #000000;">;</span><span style="color: #800080;">$params</span> = <span style="color: #0000ff;">array</span><span style="color: #000000;">( </span>"session_key" => "9XNNXe66zOlSassjSKD5gry9BiN61IUEi8IpJmjBwvU07RXP0J3c4GnhZR3GKhMHa1A=", "timestamp" => "2011-06-21 17:18:09", "format" => "json", "uid" => <span style="color: #800080;">$uid</span>,<span style="color: #000000;">);</span><span style="color: #800080;">$sign</span> = getSignature(<span style="color: #800080;">$params</span>, "27e1be4fdcaa83d7f61c489994ff6ed6");
获取签名的java代码实现方式如下所示:
<span style="color: #008000;">/**</span><span style="color: #008000;"> * 签名生成算法 * </span><span style="color: #808080;">@param</span><span style="color: #008000;"> HashMapd16797201dccc1fe78d0eb13fa114786 params 请求参数集,所有参数必须已转换为字符串类型 * </span><span style="color: #808080;">@param</span><span style="color: #008000;"> String secret 签名密钥 * </span><span style="color: #808080;">@return</span><span style="color: #008000;"> 签名 * </span><span style="color: #808080;">@throws</span><span style="color: #008000;"> IOException </span><span style="color: #008000;">*/</span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">static</span> String getSignature(HashMapd16797201dccc1fe78d0eb13fa114786 params, String secret) <span style="color: #0000ff;">throws</span><span style="color: #000000;"> IOException{ </span><span style="color: #008000;">//</span><span style="color: #008000;"> 先将参数以其参数名的字典序升序进行排序</span> Map05ad6303f369fc4ccec4412db2772d19 sortedParams = <span style="color: #0000ff;">new</span> TreeMap05ad6303f369fc4ccec4412db2772d19<span style="color: #000000;">(params); Set</span><Entry05ad6303f369fc4ccec4412db2772d19> entrys =<span style="color: #000000;"> sortedParams.entrySet(); </span><span style="color: #008000;">//</span><span style="color: #008000;"> 遍历排序后的字典,将所有参数按"key=value"格式拼接在一起</span> StringBuilder basestring = <span style="color: #0000ff;">new</span><span style="color: #000000;"> StringBuilder(); </span><span style="color: #0000ff;">for</span> (Entry05ad6303f369fc4ccec4412db2772d19<span style="color: #000000;"> param : entrys) { basestring.append(param.getKey()).append(</span>"="<span style="color: #000000;">).append(param.getValue()); } basestring.append(secret); </span><span style="color: #008000;">//</span><span style="color: #008000;"> 使用MD5对待签名串求签</span> <span style="color: #0000ff;">byte</span>[] bytes = <span style="color: #0000ff;">null</span><span style="color: #000000;">; </span><span style="color: #0000ff;">try</span><span style="color: #000000;"> { MessageDigest md5 </span>= MessageDigest.getInstance("MD5"<span style="color: #000000;">); bytes </span>= md5.digest(basestring.toString().getBytes("UTF-8"<span style="color: #000000;">)); } </span><span style="color: #0000ff;">catch</span><span style="color: #000000;"> (GeneralSecurityException ex) { </span><span style="color: #0000ff;">throw</span> <span style="color: #0000ff;">new</span><span style="color: #000000;"> IOException(ex); } </span><span style="color: #008000;">//</span><span style="color: #008000;"> 将MD5输出的二进制结果转换为小写的十六进制</span> StringBuilder sign = <span style="color: #0000ff;">new</span><span style="color: #000000;"> StringBuilder(); </span><span style="color: #0000ff;">for</span> (<span style="color: #0000ff;">int</span> i = 0; i c6b9a7224b08fe5a1c4e2e9dbd4f530e<span style="color: #808080;">///</span><span style="color: #008000;"> 计算参数签名</span><span style="color: #808080;">///</span> <span style="color: #808080;">039f3e95db2a684c7b74365531eb6044</span><span style="color: #808080;">///</span> <span style="color: #808080;">043db6d586d1018963f8fa8d9a1a8e43</span><span style="color: #008000;">请求参数集,所有参数必须已转换为字符串类型</span><span style="color: #808080;">8bb7487ae6a16a43571bc14c7fcf93c2</span><span style="color: #808080;">///</span> <span style="color: #808080;">ab260a0720ef570c5e684551feefa704</span><span style="color: #008000;">签名密钥</span><span style="color: #808080;">8bb7487ae6a16a43571bc14c7fcf93c2</span><span style="color: #808080;">///</span> <span style="color: #808080;">2363942ed0d6cd3e85bae1dffa568116</span><span style="color: #008000;">签名</span><span style="color: #808080;">f7735d9f6a7af371769ab5c16d23b2f3</span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">static</span> <span style="color: #0000ff;">string</span> getSignature(IDictionary356305bd9c416ae83e77b6b3701ce2e2 parameters, <span style="color: #0000ff;">string</span><span style="color: #000000;"> secret){ </span><span style="color: #008000;">//</span><span style="color: #008000;"> 先将参数以其参数名的字典序升序进行排序</span> IDictionary356305bd9c416ae83e77b6b3701ce2e2 sortedParams = <span style="color: #0000ff;">new</span> SortedDictionary356305bd9c416ae83e77b6b3701ce2e2<span style="color: #000000;">(parameters); IEnumerator</span><KeyValuePair356305bd9c416ae83e77b6b3701ce2e2> iterator=<span style="color: #000000;"> sortedParams.GetEnumerator(); </span><span style="color: #008000;">//</span><span style="color: #008000;"> 遍历排序后的字典,将所有参数按"key=value"格式拼接在一起</span> StringBuilder basestring= <span style="color: #0000ff;">new</span><span style="color: #000000;"> StringBuilder(); </span><span style="color: #0000ff;">while</span><span style="color: #000000;"> (iterator.MoveNext()) { </span><span style="color: #0000ff;">string</span> key =<span style="color: #000000;"> iterator.Current.Key; </span><span style="color: #0000ff;">string</span> value =<span style="color: #000000;"> iterator.Current.Value; </span><span style="color: #0000ff;">if</span> (!<span style="color: #0000ff;">string</span>.IsNullOrEmpty(key) && !<span style="color: #0000ff;">string</span><span style="color: #000000;">.IsNullOrEmpty(value)){ basestring.Append(key).Append(</span><span style="color: #800000;">"</span><span style="color: #800000;">=</span><span style="color: #800000;">"</span><span style="color: #000000;">).Append(value); } } basestring.Append(secret); </span><span style="color: #008000;">//</span><span style="color: #008000;"> 使用MD5对待签名串求签</span> MD5 md5 =<span style="color: #000000;"> MD5.Create(); </span><span style="color: #0000ff;">byte</span>[] bytes =<span style="color: #000000;"> md5.ComputeHash(Encoding.UTF8.GetBytes(basestring.ToString())); </span><span style="color: #008000;">//</span><span style="color: #008000;"> 将MD5输出的二进制结果转换为小写的十六进制</span> StringBuilder result = <span style="color: #0000ff;">new</span><span style="color: #000000;"> StringBuilder(); </span><span style="color: #0000ff;">for</span> (<span style="color: #0000ff;">int</span> i = <span style="color: #800080;">0</span>; i < bytes.Length; i++<span style="color: #000000;">) { </span><span style="color: #0000ff;">string</span> hex = bytes[i].ToString(<span style="color: #800000;">"</span><span style="color: #800000;">x</span><span style="color: #800000;">"</span><span style="color: #000000;">); </span><span style="color: #0000ff;">if</span> (hex.Length == <span style="color: #800080;">1</span><span style="color: #000000;">) { result.Append(</span><span style="color: #800000;">"</span><span style="color: #800000;">0</span><span style="color: #800000;">"</span><span style="color: #000000;">); } result.Append(hex); } </span><span style="color: #0000ff;">return</span><span style="color: #000000;"> result.ToString();}</span>
服务器接受请求后,同样对参数进行签名,如果签名相同则数据没有被修改或者丢失。
注意:计算签名时所有参数的key和value都必须先转换为对应的字符串类型,因为在HTTP请求中传递的内容都是字符串类型的,很多开发者都因为没注意到这点,直接将非字符串类型的参数的二进制值传递了进去,结果导致签名与服务端计算的不一致而出错。