百度云API鑒權(quán)總結(jié)

最近在研究百度云的一些服務(wù),處理api接口鑒權(quán)時花了不少時間,總結(jié)一下锻弓,方便大家對接:
廢話不說掩宜,直接上代碼

  • Signer.php:簽名工具類蔫骂,鑒權(quán)簽名的核心方法都在這里
  • Utils.php:封裝的工具類,鑒權(quán)牺汤,返回json數(shù)據(jù)等都在這里
  • Account.php:示例Controller辽旋,請求百度云接口

使用的tp5框架,代碼僅供參考檐迟,思路可以供大家借鑒补胚,如有不當(dāng)之處,歡迎指正

簽名工具類
Signer.php

<?php

class SignerException extends Exception
{
    function __construct($message)
    {
        parent::__construct($message, 999);
    }
}

class Signer
{
    private $ak;
    private $sk;
    private $version = "1";
    private $timestamp;
    private $expiration = 1800;
    private $method;
    private $uri;
    private $params = array();
    private $headers = array();
    private $needLog = false;

    function __construct($accessKey, $secretKey)
    {
        $this->ak = $accessKey;
        $this->sk = $secretKey;
        $date = new DateTime('now');
        $date->setTimezone(new DateTimeZone('UTC'));
        $this->timestamp = $date->format('Y-m-d\TH:i:s\Z');
    }

    public function setVersion($version)
    {
        $this->version = $version;
    }

    public function setExpiration($expiration)
    {
        $this->expiration = $expiration;
    }

    public function setMethod($method)
    {
        if (!empty($method)) {
            $this->method = strtoupper($method);
        }
    }

    public function setTimestamp($timestamp)
    {
        $this->timestamp = $timestamp;
    }

    public function setUri($uri)
    {
        $this->uri = $uri;
    }

    public function setParams($params)
    {
        $this->params = $this->normalizeParam($params);
    }

    public function setSignHeaders($headers)
    {
        $this->headers = $this->normalizeHeaders($headers);
    }

    public function beLog($needLog)
    {
        $this->needLog = $needLog;
    }

    public function genAuthorization()
    {
        $signature = $this->genSignature();
        $authStr = "bce-auth-v" . $this->version . "/" .
            $this->ak . "/" . $this->timestamp . "/" .
            $this->expiration . "/" . $this->getSignedHeaderNames() . "/" . $signature;
        return $authStr;
    }

    public function genSignature()
    {
        if (empty($this->method)) {
            throw new SignerException("method is null or empty");
        }
        $signingKey = $this->genSigningKey();
        $this->signerLog("signingKey:" . $signingKey, __LINE__, __FILE__);
        $authStr = $this->method . "\n" .
            $this->getCanonicalURI() . "\n" .
            $this->getCanonicalParam() . "\n" .
            $this->getCanonicalHeaders();
        $this->signerLog("auth str:" . $authStr, __LINE__, __FILE__);
        return $this->sha256($signingKey, $authStr);
    }

    public function genSigningKey()
    {
        if (empty($this->ak)) {
            throw new SignerException("access key is null or empty");
        }
        if (empty($this->sk)) {
            throw new SignerException("secret key is null or empty");
        }
        if (empty($this->version)) {
            throw new SignerException("version is null or empty");
        }
        if (empty($this->timestamp)) {
            throw new SignerException("timestamp is null or empty");
        }
        if (empty($this->expiration)) {
            throw new SignerException("expiration is null or empty");
        }
        $authStr = "bce-auth-v" . $this->version . "/" . $this->ak . "/" .
            $this->timestamp . "/" . $this->expiration;
        return $this->sha256($this->sk, $authStr);
    }

    public function getCanonicalParam()
    {
        if (empty($this->params)) {
            return "";
        }
        $arryLen = count($this->params);
        $canonicalParams = "";
        foreach ($this->params as $key => $value) {
            if (is_array($value)) {
                $num = count($value);
                if (count($value) == 0) {
                    $canonicalParams = $canonicalParams . $key . "=";
                } else {
                    foreach ($value as $item) {
                        $canonicalParams = $canonicalParams . $key . "=" . $item;
                        if ($num > 1) {
                            $canonicalParams = $canonicalParams . "&";
                            $num--;
                        }
                    }
                }
            } else {
                $canonicalParams = $canonicalParams . $key . "=" . $value;
            }
            if ($arryLen > 1) {
                $canonicalParams = $canonicalParams . "&";
                $arryLen--;
            }
        }
        return $canonicalParams;
    }

    public function getCanonicalURI()
    {
        if (empty($this->uri)) {
            throw new SignerException("uri is null or empty");
        }
        $newUri = $this->dataEncode($this->uri, true);
        if (strpos($newUri, "/") === 0) {
            return $newUri;
        }
        return "/" . $newUri;
    }

    public function getCanonicalHeaders()
    {
        if (empty($this->headers) || !array_key_exists("host", $this->headers)) {
            throw new SignerException("host not in headers");
        }
        $canonicalHeaders = "";
        $strArry = array();
        foreach ($this->headers as $key => $value) {
            if (empty($value)) {
                continue;
            }
            $strArry[] = $this->dataEncode($key, false) . ":" . $value;
        }
        $arryLen = count($strArry);
        for ($i = 0; $i < $arryLen; $i++) {
            if ($i < $arryLen - 1) {
                $canonicalHeaders = $canonicalHeaders . $strArry[$i] . "\n";
                continue;
            }
            $canonicalHeaders = $canonicalHeaders . $strArry[$i];
        }
        return $canonicalHeaders;
    }

    private function sha256($key, $data)
    {
        return hash_hmac('sha256', $data, $key);
    }

    private function dataEncode($data, $isPath)
    {
        if (empty($data)) {
            return "";
        }
        $encode = mb_detect_encoding($data, array("ASCII", "UTF-8", "GB2312", "GBK", "BIG5"));
        if ($encode != "UTF-8") {
            $data = $code1 = mb_convert_encoding($data, 'utf-8', $encode);
        }
        $encodeStr = rawurlencode($data);
        if ($isPath) {
            $encodeStr = str_replace("%2F", "/", $encodeStr);
        }
        return $encodeStr;
    }

    private function normalizeHeaders($headers)
    {
        $newArray = array();
        if (empty($headers)) {
            return $newArray;
        }
        foreach ($headers as $key => $value) {
            $newKey = strtolower($key);
            if (empty($newKey)) {
                continue;
            }
            $newArray[$newKey] = $this->dataEncode(trim($value), false);
        }
        ksort($newArray);
        return $newArray;
    }

    private function normalizeParam($params)
    {
        $newArray = array();
        if (empty($params)) {
            return $newArray;
        }
        foreach ($params as $key => $value) {
            if (empty($key) || strtolower($key) == "authorization") {
                continue;
            }
            if (is_array($value)) {
                $newSubArray = array();
                foreach ($value as $item) {
                    $newSubArray[] = $this->dataEncode($item, false);
                }
                sort($newSubArray);
                $newArray[$this->dataEncode($key, false)] = $newSubArray;
            } else {
                $newArray[$this->dataEncode($key, false)] = $this->dataEncode($value, false);
            }
        }
        ksort($newArray);
        return $newArray;
    }

    private function getSignedHeaderNames()
    {
        $arryLen = count($this->headers);
        $headerNames = "";
        foreach ($this->headers as $key => $value) {
            $headerNames = $headerNames . $key;
            if ($arryLen > 1) {
                $headerNames = $headerNames . ";";
                $arryLen--;
            }
        }
        return $headerNames;
    }

    private function signerLog($content, $line, $file)
    {
        if ($this->needLog) {
            error_log($file . ":" . $line . ":[" . $content . "]\n", 3, "./signer_log");
        }
    }
}

?>

封裝的工具類追迟,集成了常用的方法
Utils.php

<?php

/**
 * Created by PhpStorm.
 * User: 王中陽
 * Date: 2019/7/24
 * Time: 9:45
 */
require 'Signer.php';
define('AK', "your ak");
define('SK', "your sk");
define('HOST', "sem.baidubce.com"); //按百度要求換內(nèi)容
define('BASE_URL', "http://sem.baidubce.com/");//按百度要求換內(nèi)容
define('HTTP_Method', "POST");//按百度要求換內(nèi)容

class Utils
{
    //公共header  注意:我對接的是百度信息流推廣api溶其,header應(yīng)根據(jù)百度云各服務(wù)的要求進(jìn)行修改
    static function Header()
    {
        return [
            'opUsername' => 'xxxxxxx',
            'opPassword' => 'xxxxxxx',
            'tgUsername' => 'xxxxxxx',
            'tgPassword' => 'xxxxxxx',
            'bceUser' => 'xxxxxxx',
        ];
    }

    //請求百度
    static function curl_post($url, $body, $auth)
    {
        //處理請求體
        $files = [
            'header' => self::Header(),
            'body' => $body
        ];
        $files = json_encode($files, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);

        $ch = curl_init();
        curl_setopt($ch, CURLOPT_POST, 1);
        curl_setopt($ch, CURLOPT_URL, $url);


        curl_setopt($ch, CURLOPT_POSTFIELDS, $files);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
        curl_setopt($ch, CURLOPT_HTTPHEADER, array(
                'Content-Type: application/json',
                'host:sem.baidubce.com',  //改成你的數(shù)據(jù)
                'authorization:' . $auth,
                'accept-encoding:gzip, deflate',
                'accept:*/*'
            )
        );
        $response = curl_exec($ch);
        $request = curl_getinfo($ch, CURLINFO_HEADER_OUT);
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        self::result($httpCode, $response);
    }

    //校驗權(quán)限
    static function gen_auth($uri = "")
    {
        $headers = array();
        $headers['host'] = HOST;
        $signer = new \Signer(AK, SK);
        $signer->setMethod(HTTP_Method);
        $signer->setUri($uri);
        $signer->setSignHeaders($headers);
        try {
            $signature = $signer->genAuthorization();
            return $signature;
        } catch (SignerException $e) {
            echo $e->getMessage();
            return;
        }
    }

    //返回結(jié)果
    static public function result($errno = 0, $data = '')
    {
        header("Content-type: application/json;charset=utf-8");

        $errno = intval($errno);

        //注意:這里可能不滿足你的項目 根據(jù)百度返回結(jié)果做修改 或者不用我這種處理方式
        //json轉(zhuǎn)數(shù)組
        $data = json_decode($data, true);
        if (isset($data['header']['failures'][0])) {
            $message = $data['header']['failures'][0]['message'];
        } else {
            $message = 'success';
        }

        $json = json_encode($data['body']['data'][0]);
        $result = array(
            'errno' => 1,
            'message' => $message,
            'data' => json_encode($data['body']['data'][0]),//處理百度 返回結(jié)果
        );
        echo json_encode($result, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
        exit;
    }

    /**
     * 時間 日期
     */
    static public function ymd($time)
    {
        return date("Y-m-d", $time);
    }
}

業(yè)務(wù)層controller:百度api在請求接口的同時做權(quán)限校驗
Account.php

<?php
namespace app\index\controller;

require 'Utils.php';

class Account
{
    /**
     * 獲得賬號信息
     */
    public function info()
    {
        $uri = "v1/feed/cloud/AccountFeedService/getAccountFeed";
        $auth = \Utils::gen_auth($uri);
        $url = BASE_URL . $uri;

        $body = [
            'accountFeedFields' => [
                'userId',
                'balance',
                'budget',
                'balancePackage',
                'userStat',
                'uaStatus',
                'validFlows',
            ],
        ];
        //返回數(shù)據(jù)集成在Utils中
        \Utils::curl_post($url, $body, $auth);
    }

思路僅供參考。如有更好的處理方式敦间,歡迎賜教瓶逃。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市廓块,隨后出現(xiàn)的幾起案子厢绝,更是在濱河造成了極大的恐慌,老刑警劉巖带猴,帶你破解...
    沈念sama閱讀 219,539評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件昔汉,死亡現(xiàn)場離奇詭異,居然都是意外死亡拴清,警方通過查閱死者的電腦和手機(jī)靶病,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,594評論 3 396
  • 文/潘曉璐 我一進(jìn)店門会通,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人娄周,你說我怎么就攤上這事渴语。” “怎么了昆咽?”我有些...
    開封第一講書人閱讀 165,871評論 0 356
  • 文/不壞的土叔 我叫張陵驾凶,是天一觀的道長。 經(jīng)常有香客問我掷酗,道長调违,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,963評論 1 295
  • 正文 為了忘掉前任泻轰,我火速辦了婚禮技肩,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘浮声。我一直安慰自己虚婿,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,984評論 6 393
  • 文/花漫 我一把揭開白布泳挥。 她就那樣靜靜地躺著然痊,像睡著了一般。 火紅的嫁衣襯著肌膚如雪屉符。 梳的紋絲不亂的頭發(fā)上剧浸,一...
    開封第一講書人閱讀 51,763評論 1 307
  • 那天,我揣著相機(jī)與錄音矗钟,去河邊找鬼唆香。 笑死,一個胖子當(dāng)著我的面吹牛吨艇,可吹牛的內(nèi)容都是我干的躬它。 我是一名探鬼主播,決...
    沈念sama閱讀 40,468評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼东涡,長吁一口氣:“原來是場噩夢啊……” “哼冯吓!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起软啼,我...
    開封第一講書人閱讀 39,357評論 0 276
  • 序言:老撾萬榮一對情侶失蹤桑谍,失蹤者是張志新(化名)和其女友劉穎延柠,沒想到半個月后祸挪,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,850評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡贞间,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,002評論 3 338
  • 正文 我和宋清朗相戀三年贿条,在試婚紗的時候發(fā)現(xiàn)自己被綠了雹仿。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,144評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡整以,死狀恐怖胧辽,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情公黑,我是刑警寧澤邑商,帶...
    沈念sama閱讀 35,823評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站凡蚜,受9級特大地震影響人断,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜朝蜘,卻給世界環(huán)境...
    茶點故事閱讀 41,483評論 3 331
  • 文/蒙蒙 一恶迈、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧谱醇,春花似錦暇仲、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,026評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至煮剧,卻和暖如春桅狠,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背轿秧。 一陣腳步聲響...
    開封第一講書人閱讀 33,150評論 1 272
  • 我被黑心中介騙來泰國打工中跌, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人菇篡。 一個月前我還...
    沈念sama閱讀 48,415評論 3 373
  • 正文 我出身青樓漩符,卻偏偏與公主長得像,于是被迫代替她去往敵國和親驱还。 傳聞我的和親對象是個殘疾皇子嗜暴,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,092評論 2 355