导语
该系列其他篇章:
V3手动鉴权失败之Nodejs篇
V3手动鉴权失败之Go篇
V3手动鉴权失败之Python篇
V3手动鉴权失败之Java篇
V3手动鉴权失败之C#篇
腾讯云 API 全新升级 3.0 ,该版本进行了性能优化且全地域部署、支持就近和按地域接入、访问时延下降显著,接口描述更加详细、错误码描述更加全面、SDK增加接口级注释,让您更加方便快捷的使用腾讯云产品。人脸识别、文字识别,语音识别等众多产品均已接入云API 3.0。
腾讯云API为了更好的让用户接入,已经封装好了多种语言的SDK,只需用户传入SecrectId、SectectKey以及接口入参,即可完成接口鉴权和请求发送,具体包括Python SDK、Java SDK、PHP SDK、Go SDK、NodeJS SDK、.NET SDK。
案例背景
在某些情况,用户需要实现手动接口鉴权,虽然官网文档已有详细的接口鉴权流程,但是由于:
1.V3手动鉴权步骤较为复杂;
2.官网某些demo代码无法直接下载运行,仍需简单调整;
3.官网文档的demo代码覆盖面有限,没有包括全量上述六类后端语言;
基于此,很多用户只能自己尝试手动鉴权,但都返回“鉴权失败”,从而无法调通接口。
原因分析
从宏观上看,“鉴权失败”要关注两个阶段:
1. 整体的接口鉴权是否正确;
2. 模拟的鉴权请求的发送是否正确;
从历史问题回顾,有客户曾经出现接口鉴权时而成功,时而失败的情况,排查了整体的鉴权过程,完全正确,但是也的确复现了客户的问题。后来发现,用户在鉴权完成后,发送具体的请求时,传入的时间戳timestamp没有实时更新导致了报错。
解决方案
为了帮助客户更简单、更快捷地完成接口手动鉴权,并成功发送鉴权请求,将通过一系列文章专门讲解各个后端语言的手动鉴权&发送请求的可执行demo代码,助力客户快速接入。
本期将以调用人脸识别的DetectFace接口为例,详叙PHP语言
demo。
前期准备
PHP集成开发环境:笔者使用wamp集成环境,在sourceforge中下载并安装即可。安装后,双击wampserver64启动服务。
SecrectId和SecretKey:接口鉴权的密钥。可以把SecretId理解成“账号”,把SecretKey理解成“密码”。在自己的腾讯云官网控制台获取:访问管理 -> 访问密钥 -> API密钥管理。
手动鉴权相关文档:请求结构、公共参数、V3接口鉴权
具体代码
运行php语言代码,即可完成v3鉴权,并发送http请求,收到具体的response响应。有两种运行方式:
方法一:进入wamp安装目录下,找到php.exe的目录,打开cmd控制台,执行php.exe php_v3.php,例如:
代码语言:javascript复制D:ProgramInstallwamp64binphpphp5.6.40>php.exe D:Codetestphp_v3.php
方法二:进入wamp安装目录,找到www目录,将php_v3.php复制到www目录下,在浏览器访问127.0.0.1/php_v3.php即可:
上述方法中涉及的php_v3.php代码如下,只需要简单复制,然后输入自己的SecretId和SecretKey两个字段即可:
代码语言:javascript复制<?php
class Face{
const SecretId = "xxx"; //填写自己的secretId
const SecretKey = "xxx"; //填写自己的secretKey
const Url = "https://iai.tencentcloudapi.com";
//算法
const Algo = "sha256";
//规范请求串
const HTTPRequestMethod = "POST";
const CanonicalURI = "/";
const CanonicalQueryString = "";
const CanonicalHeaders = "content-type:application/json; charset=utf-8nhost:iai.tencentcloudapi.comn";
const SignedHeaders = "content-type;host";//参与签名的头部信息
//签名字符串
const Algorithm = "TC3-HMAC-SHA256";
const Service = "iai";
const Stop = "tc3_request";
/**
* 人脸检测与分析
*/
public function getFace(){
$param = [
//'MinFaceSize' => 20,
'Url' => "https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1599650382393&di=54ae4347050e54331809233159f4adaf&imgtype=0&src=http://aimg8.dlszyht.net.cn/ueditor/image/772/1542282/1525679281357881.jpg",
'FaceModelVersion' => '3.0'
];
return self::getCommonPostRequest("DetectFace", $param);
}
/**
* 鉴权
* @param string $action 方法
* @param array $param 参数
* @param string $version 版本号
* @return array
*/
private static function getCommonPostRequest( $action, array $param = [], $version = "2018-03-01")
{
//时间戳
$timeStamp = time();
//参数转化Json
$paramJson = json_encode($param);
//规范请求串
$hashedRequestPayload = self::HashEncryption($paramJson);
$canonicalRequest = self::HTTPRequestMethod . "n" .
self::CanonicalURI . "n" .
self::CanonicalQueryString . "n" .
self::CanonicalHeaders . "n" .
self::SignedHeaders . "n" .
$hashedRequestPayload;
//签名字符串
$date = gmdate("Y-m-d", $timeStamp);//UTC 0时区的值
$credentialScope = $date . "/". self::Service . "/". self::Stop;
$hashedCanonicalRequest = self::HashEncryption($canonicalRequest);
$stringToSign = self::Algorithm . "n" .
$timeStamp . "n" .
$credentialScope . "n" .
$hashedCanonicalRequest;
//计算签名
$secretDate = self::HashHmacSha256Encryption($date, 'TC3' . self::SecretKey);
$secretService = self::HashHmacSha256Encryption(self::Service, $secretDate);
$secretSigning = self::HashHmacSha256Encryption(self::Stop, $secretService);
//签名
$signature = self::HashHmacSha256Encryption($stringToSign, $secretSigning, false);
$authorization = self::Algorithm . ' ' .
'Credential=' . self::SecretId . '/' . $credentialScope . ', ' .
'SignedHeaders=' . self::SignedHeaders . ', ' .
'Signature=' . $signature;
//Header头部
$headers = [
"Authorization: $authorization",
"Host: iai.tencentcloudapi.com",
"Content-Type: application/json; charset=utf-8",
"X-TC-Action: $action",
"X-TC-Version: $version",
"X-TC-Timestamp: $timeStamp",
//"X-TC-Region: ap-chengdu"
];
//请求
$response = self::get_curl_request(self::Url, $paramJson, self::HTTPRequestMethod, $headers);
//解析
if (!$response) {
return ['code' => 0, 'codeError' => '1002', 'msg' => 'Interface request failed'];
}
$response = json_decode($response, true);
if (!isset($response['Response'])) {
return ['code' => 0, 'codeError' => '1003', 'msg' => 'Response error'];
}
if (isset($response['Response']['Error'])) {
return [
'code' => 0
, 'codeError' => $response['Response']['Error']['Code']
, 'msg' => $response['Response']['Error']['Message']
, 'RequestId' => $response['Response']['RequestId']
];
} else {
return ['code' => 1, 'msg' => 'ok', 'data' => $response['Response']];
}
}
private static function HashEncryption($sign){
return strtolower(hash(self::Algo, $sign));
}
private static function HashHmacSha256Encryption($sign, $key, $flag = true){
return hash_hmac(self::Algo, $sign, $key, $flag);
}
/**
* @param $url
* @param array $param
* @param string $mothod
* @param array $headers
* @param int $return_status
* @param int $flag
* @return array|bool|string
*/
public static function get_curl_request($url, $param = [], $mothod = 'POST', $headers = [], $return_status = 0, $flag = 0)
{
$ch = curl_init();
if (!$flag) {
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
}
//内网需要开启代理
//curl_setopt($ch, CURLOPT_PROXY, "127.0.0.1");
//curl_setopt($ch, CURLOPT_PROXYPORT, 12639);
curl_setopt($ch, CURLOPT_TIMEOUT, 6);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
if (strtolower($mothod) == 'post') {
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $param);
} else {
$url = $url . "?" . http_build_query($param);
}
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 2);
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
$ret = curl_exec($ch);
$code = curl_getinfo($ch);
curl_close($ch);
if ($return_status == "1") {
return array($ret, $code);
}
return $ret;
}
}
$model = new Face();
$data = $model->getFace();
var_dump($data);