最近在研究百度云的一些服务,处理api接口鉴权时花了不少时间,总结一下,方便大家对接:
废话不说,直接上代码
- Signer.php:签名工具类,鉴权签名的核心方法都在这里
- Utils.php:封装的工具类,鉴权,返回json数据等都在这里
- Account.php:示例Controller,请求百度云接口
使用的tp5框架,代码仅供参考,思路可以供大家借鉴,如有不当之处,欢迎指正
签名工具类
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"); //按百度要求换内容
define('BASE_URL', "http://sem.baidubce.com/");//按百度要求换内容
define('HTTP_Method', "POST");//按百度要求换内容
class Utils
{
//公共header 注意:我对接的是百度信息流推广api,header应根据百度云各服务的要求进行修改
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', //改成你的数据
'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);
}
//校验权限
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;
}
}
//返回结果
static public function result($errno = 0, $data = '')
{
header("Content-type: application/json;charset=utf-8");
$errno = intval($errno);
//注意:这里可能不满足你的项目 根据百度返回结果做修改 或者不用我这种处理方式
//json转数组
$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]),//处理百度 返回结果
);
echo json_encode($result, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
exit;
}
/**
* 时间 日期
*/
static public function ymd($time)
{
return date("Y-m-d", $time);
}
}
业务层controller:百度api在请求接口的同时做权限校验
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',
],
];
//返回数据集成在Utils中
\Utils::curl_post($url, $body, $auth);
}
思路仅供参考。如有更好的处理方式,欢迎赐教。
网友评论