PHP實現API接口簽名驗證

項目需要向外部提供接口,供第三方網站調用,爲了保證傳輸數據的安全性,給項目添加了簽名認證的機制,過程大致如下:

一、由我們平臺給第三方頒發一個appId和一個appSecret,appId用來傳輸,appSecret用來生成簽名

二、第三方通過拼接appSecret生成簽名sign,第三方將數據和appId一起傳給我們平臺

三、我們平臺接收到數據後根據接收到的數據用同樣的算法生成簽名,通過比對簽名確定來進行數據來源的有效性確認

部分代碼如下:

一、appId和appSecret的存儲表如下:

CREATE TABLE `encrypt` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `public_key` varchar(255) NOT NULL COMMENT '公鑰',
  `screct_key` varchar(255) NOT NULL COMMENT '私鑰',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4;

二、生成appId和appSecret的方法如下:

    /**
     * 生成Key
     * @access public
     * @return array
     */
    public static function makeKey()
    {
        $appId = strrev(microtime(true)*10000);
        $appSecret = md5(md5($appId.'awen').'awen');
        $model = new \common\models\Encrypt();
        $model->public_key = $appId;
        $model->screct_key = $appSecret;
        $model->save();
        return [
            'appId' => $appId,
            'appSecret' => $appSecret,
        ];
    }

三、簽名生成方法:

1. 對所有請求參數進行字典升序排列; 
2. 將以上排序後的參數表進行字符串連接,如key1value1key2value2key3value3...keyNvalueN; 
3. app secret作爲後綴,對該字符串進行SHA-1計算,並轉換成16進制編碼; 
4. 轉換爲全大寫形式後即獲得簽名串

具體簽名生成與對比的代碼如下

    /**
     * 驗證簽名有效性
     * @access public
     * @param array params 參數
     * @param sting sign 簽名
     * @param sting appId
     * @return bool 驗證結果
     */
    public static function checkSign($params,$sign,$appId)
    {
        //根據appId去數據庫找到對應的appSecret
        $appSecret = self::getAppSecret($appId);
        // 1. 對加密數組進行字典排序 防止因爲參數順序不一致而導致下面拼接加密不同
        ksort($params);
        // 2. 將Key和Value拼接
        $str = "";
        foreach ($params as $k => $v) {
            $str.= $k.$v;
        }
        //3. 通過sha1加密並轉化爲大寫
        //4. 大寫獲得簽名
        $restr=$str.$appSecret;
        $signRes = strtoupper(sha1($restr));

        if($signRes == $sign){
            return true;
        }else{
            return false;
        }
    }

四、在basecontroller中添加簽名驗證代碼

    /**
     * 驗證簽名
     */
    public function beforeAction($action)
    {
        $post = @file_get_contents('php://input');
        \Yii::$app->log->targets[0]->logFile = \Yii::getAlias('@runtime').DIRECTORY_SEPARATOR.'logs'.DIRECTORY_SEPARATOR.'outside.log';
        \Yii::trace('接受的數據爲:'.$post);
        //解析成數組
        $post =  json_decode( $post, true );
        $data = $post['data'];
        if(!$data){
            $result = ['status'=> 0, 'message'=>'缺少參數data'];
            return \Yii::$app->response->data = $result;
        }
        $appId = $post['appId'];
        if(!$appId){
            $result = ['status'=> 0, 'message'=>'缺少參數appId'];
            return \Yii::$app->response->data = $result;
        }
        parse_str($data,$params);
        $sign = $params['sign'];
        unset($params['sign']);
        $signCheck = Encrypt::checkSign($params,$sign,$appId);
        if($signCheck){
            $result = ['status'=> 1, 'message'=>'數據正常','params'=>$params,'appId'=>$appId];
        }else{
            $result = ['status'=> 0, 'message'=>'簽名錯誤','params'=>$params,'appId'=>$appId];
        }

        return \Yii::$app->response->data = $result;
    }

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章