php簽名認證

  1、概述

        開年第一篇,該篇主要講述了接口開發中,如何安全認證、如何用php簽名認證。javascript

  2、說說歷史

        簽名認證是什麼?爲何要作簽名認證?簽名認證哪裏會用到?no、no、no.....是否是,是否是,一會兒疑問就這麼多了!沒事兒,經過追溯歷史,咱們來明白這些。php

     一、簽名認證是什麼?

        數字簽名是一種相似寫在紙上的普通的物理簽名,可是使用了公鑰加密領域的技術實現,用於鑑別數字信息的方法。一套數字簽名一般定義兩種互補的運算,一個用於簽名,另外一個用於驗證。
數字簽名,就是隻有信息的發送者才能產生的別人沒法僞造的一段數字串,這段數字串同時也是對信息的發送者發送信息真實性的一個有效證實。
        在這個以「數據爲生命」的時代,每個開發商都儘量的收集客戶的數據創建本身的BI庫,各系統、各平臺間數據的傳輸和調用變得很是廣泛且很是重要;那麼做爲開發人員,咱們不但要防止系統被攻擊被入侵,咱們還要確保數據的安全和完整

       二、爲何要作簽名認證?

        在使用http或者soap傳輸數據的時候,簽名做爲其中一個參數,能夠起到關鍵做用:一、鑑權(經過客戶的密鑰,服務端的密鑰匹配);二、數據防篡改(參數是明文傳輸,將參數及密鑰加密做爲簽名與服務器匹配);下面來分析下具體的方式: 

        將請求參數中的各個鍵值對按照key的字符串順序升序排列(大小寫敏感),把key和value拼成一串以後最後加上密鑰,組成key1value1key2value2PRIVATEKEY的格式,轉成utf-8編碼的字節序列後計算md5,做爲請求的簽名。計算出來的簽名串應當全爲小寫形式。若是某個參數的值爲空,則此參數不參與簽名。 html

      三、簽名認證哪裏會用到?

        最多見的使咱們在開發接口的時候,爲了避免被非法訪問,每每咱們會作簽名認證,好比支付接口......java

        而後就是第三方平臺的開發,好比微信公衆平臺......算法

  3、流程分析

      客戶端:首先咱們爲提供給用戶一份接口文檔,文檔裏面咱們給用戶提供api地址、簽名算法解析過程、數聽說明。json

        服務端:進行客戶端檢驗,一般就是參數個數、格式驗證,app_key驗證(這個是博主這裏使用的,這個app_key會交給用戶,用戶訪問接口的時候必須帶上該app_key),簽名結果驗證。api

      以我此次寫的爲例子來描述一下整個流程:安全

        用戶參照咱們提供的接口文檔,請求咱們的接口,用戶按照咱們接口說明的簽名算法生成簽名串做爲參數,而後帶上必要的參數(GET、POST),若是服務端驗證成功,則返回真實數據。服務器

    

        在服務端:咱們首要驗證提交參數的個數、格式是否正確,而後咱們經過提交的參數用簽名算法生成一個簽名串,最後服務端生成的簽名串和客戶端提交過來的簽名串進行比較,成功返回真實數據。微信

  4、上代碼

  1 <?php 
  2 namespace ceshi;
  3 
  4 /**
  5  * 簽名認證算法
  6  * HMAC-SHA256加密方式
  7  * 登陸認證加入access_token:access-token經過登陸接口去獲取,經過刷新接口去刷新,須要注意返回的過時時間,要在過時時間以前刷新從新獲取access-token。目前約定全部接口都必須傳access-token,也就是用戶必須先登陸才能夠看到相關內容
  8  * 隨機函數可換更好的方法-本實例隨機函數比較簡單,隨機性不夠
  9  * ApiSign類做爲服務端,類之外的代碼做爲客戶端示例代碼
 10  * author jiechengyang https://www.cnblogs.com/YangJieCheng/
 11  */
 12 class ApiSign 
 13 {
 14     CONST DELAY_TIME = 2000;
 15     CONST ACCESS_TOCEN_PATH = './access_token';
 16 
 17     private $_config = [];
 18     protected $AppKey = 'voBVVQxfMxDmhuxV70';
 19     protected $AppSecret = 'QJF5P8qWFJakF9Ve89ZcIstHKbkt5fVA';
 20     protected $timeout = 300;
 21     protected $algo = 'sha256';
 22     public $loginCheck = false;
 23 
 24     public function __construct($config, $loginCheck)
 25     {
 26         $this->_config = $config;
 27         empty($this->AppKey) && $this->AppKey = $this->generateRandomString();
 28         empty($this->AppSecret) && $this->AppSecret = $this->generateRangeNum();
 29         $this->loginCheck = $loginCheck;
 30         if ($this->init()) {
 31             echo '<span style="color:red" id="sign_success">恭喜,簽名認證成功</span><script type="text/javascript"></script>';
 32             echo <<<JS
 33                 <script>
 34                     var colors = ['#ffff00', '#ff66ff', '#99cc33', '#66ff33', '#000000', '#FF83FA', '#CAE1FF'];
 35                     var tmp = 0;
 36                     var timer = setInterval(colorChanage, 1000);
 37                     function colorChanage()
 38                     {
 39                         if(tmp == colors.length) {
 40                             tmp = 0;
 41                         }
 42                         document.getElementById('sign_success').style.color = colors[tmp++];
 43                     }
 44                 </script>
 45 JS;
 46         }
 47     }
 48 
 49     protected function init()
 50     {
 51         if (!isset($this->_config['_key']) 
 52             || !isset($this->_config['_sign'])
 53             || !isset($this->_config['_time'])
 54             || !isset($this->_config['_nonce'])
 55             || strlen($this->_config['_nonce']) !== 32
 56             ||  !is_numeric($this->_config['_time'])) {
 57                 $this->callback(['message' => '請求參數不全, 或參數不規範'], 'error');
 58             }
 59 
 60         if (!isset($this->_config['_time']) || $this->getIsTimeOut()) {
 61             $this->callback(['message' => '請求超時'], 'error');
 62         }
 63 
 64         // 客戶端驗證
 65         $requestSignature = $this->_config['_sign'];
 66         $requestSignature = str_replace(' ', '+', $requestSignature);
 67         if($this->_config['_key'] !== $this->AppKey) {
 68             $this->callback(['message' => '非法的app_key'], 'error');
 69         }
 70 
 71         $signature = $this->generateSign($this->_config);
 72         if ($requestSignature != $signature) {
 73             $this->callback(['message' => '簽名錯誤'], 'error');
 74         }
 75 
 76           $accessToken = $this->getAccessToken();
 77 
 78           // 用戶登陸token驗證
 79           if ($this->loginCheck && $accessToken != $this->_config['access_token']) {
 80             $this->callback(['message' => 'access_token錯誤'], 'error');
 81           }
 82 
 83         return true;
 84     }
 85 
 86     private function callback($json=[], $status='ok')
 87     {
 88         $config = [
 89             'ok' => [200, '操做成功'],
 90             'error' => [300, '操做失敗'],
 91             'timeout' => [300, '操做超時'],
 92         ];
 93         $json['statusCode'] = $config[$status][0];
 94         !isset($json['message']) && $json['message'] = $config[$status][1];
 95         echo json_encode($json);
 96         exit;
 97     }
 98 
 99     private function getIsTimeOut()
100     {
101         if (abs($this->_config['_time'] - time()) > $this->timeout) {
102             return true;
103         }
104 
105         return false;
106     }
107 
108    /**
109      * 生成隨便數
110      */
111     public  function generateRangeNum($length = 32, $isToLower = false)
112     {
113         $str = $this->generateRandomString($length);
114         if ($isToLower) {
115             $str = strtolower($str);
116         }
117 
118         return $str;
119     }
120 
121     private function generateRandomString($length = 10) { 
122         $characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'; 
123         $randomString = ''; 
124         for ($i = 0; $i < $length; $i++) { 
125             $randomString .= $characters[rand(0, strlen($characters) - 1)]; 
126         } 
127 
128         return $randomString; 
129     }
130 
131     protected function generateSign($params)
132     {
133         if (isset($params['_sign'])) {
134             unset($params['_sign']);
135         }
136 
137         if($this->loginCheck && isset($params['access_token'])) {
138             unset($params['access_token']);
139         }
140 
141         ksort($params);
142         $str = '';
143         foreach ($params as $key => $value) {
144             $str .= $key . '=' . $value . '&';
145         }
146 
147         $str = rtrim($str, '&');
148 
149         return hash_hmac($this->algo, $str, $this->AppSecret, false);
150     }
151 
152     public  function getAccessToken()
153     {
154 
155         if (!file_exists(self::ACCESS_TOCEN_PATH)) {
156             $json = ['value' => $this->_config['access_token'], 'expires_in' => 7200, 'time' => time()];
157             file_put_contents(self::ACCESS_TOCEN_PATH, json_encode($json));
158             return $this->_config['access_token'];
159         }
160 
161         $accessToken = json_decode(file_get_contents(self::ACCESS_TOCEN_PATH), true);
162         if (time() - $accessToken['time'] > $accessToken['expires_in'] - self::DELAY_TIME) {
163             $json = ['value' => $this->_config['access_token'], 'expires_in' => 7200, 'time' => time()];
164             file_put_contents(self::ACCESS_TOCEN_PATH, json_encode($json));
165             return $this->_config['access_token'];
166         }    
167 
168         return $accessToken['value'];    
169 
170     }
171 }
172 
173 /**
174  * 生成隨便數
175  */
176 function generateRangeNum($length = 32, $isToLower = false)
177 {
178     $str = generateRandomString($length);
179     if ($isToLower) {
180         $str = strtolower($str);
181     }
182 
183     return $str;
184 }
185 
186 function generateRandomString($length = 10) { 
187     $characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'; 
188     $randomString = ''; 
189     for ($i = 0; $i < $length; $i++) { 
190         $randomString .= $characters[rand(0, strlen($characters) - 1)]; 
191     } 
192 
193     return $randomString; 
194 }
195 
196 function generateSign($algo, $params, $AppSecret)
197 {
198     if (isset($params['_sign'])) {
199         unset($params['_sign']);
200     }
201 
202     ksort($params);
203     $str = '';
204     foreach ($params as $key => $value) {
205         $str .= $key . '=' . $value . '&';
206     }
207 
208     $str = rtrim($str, '&');
209 
210     return hash_hmac($algo, $str, $AppSecret, false);
211 }
212 
213 header("content-type:text/html;charset:utf-8");
214 $algo = 'sha256';
215 $AppKey = 'voBVVQxfMxDmhuxV70';
216 $AppKey = 'Rd1bW719zRbCXOBx3L';//---用於公司項目測試
217 $AppSecret = 'QJF5P8qWFJakF9Ve89ZcIstHKbkt5fVA';
218 $AppSecret = '73ZBAnbwVPDu0dvdlYE0RMvzsbhehejd';//---用於公司項目測試
219 $nonce = generateRangeNum(32);
220 $nonce = 'kj4ESP2Qcngaj3eAjpnrhCQR9g4yKnTM';//---用於公司項目測試
221 $loginCheck = false;
222 $params = [
223     '_key' => $AppKey,
224     '_time' => time(),
225     '_nonce' => $nonce,
226 ];
227 $sign = generateSign($algo, $params, $AppSecret);
228 echo $params['_time'],'<hr/>';//---用於公司項目測試
229 echo $sign;exit;//---用於公司項目測試
230 $params['_sign'] = $sign;
231 
232 $loginCheck && $params['access_token'] = generateRangeNum(16);
233 // echo '<pre>';print_r($params);
234 $apiSignModel = new ApiSign($params, $loginCheck);
相關文章
相關標籤/搜索