验证对 Azure 批处理 REST 接口的请求 (php)
Authenticating requests to azure's batch REST interface (php)
我正在尝试从我的 php 服务器对 azures 批处理 REST 接口进行身份验证。
根据文档 (https://docs.microsoft.com/en-us/rest/api/batchservice/authenticate-requests-to-the-azure-batch-service) 我想出了这个函数:
use GuzzleHttp\Client;
const BATCH_ACCOUNT_NAME = "myAccount";
const BATCH_ACCOUNT_KEY = "mySuperSecretKey";
const BATCH_ENDPOINT = "https://myAccount.theRegion.batch.azure.com";
// Pool and Job constants
const POOL_ID = "MyTestPool";
const POOL_VM_SIZE = "STANDARD_A1_v2";
private function createPoolIfNotExists()
{
echo "-- creating batch pool --\n\n";
$client = new Client();
$body = [
"id" => POOL_ID,
"vmSize" => POOL_VM_SIZE,
];
$contentType = "application/json;odata=minimalmetadata";
$apiVersion = "2021-06-01.14.0";
$ocpDate = Carbon::now('UTC')->toString("R");
$signature = $this->createRidiculouslyOverComplicatedSignature(
"POST",
$contentType,
$apiVersion,
$ocpDate,
$body
);
$response = $client->post(BATCH_ENDPOINT . "/pools?api-version={$apiVersion}", [
'json' => $body,
'headers' => [
"ocp-date" => $ocpDate,
"Authorization" => "SharedKey " . BATCH_ACCOUNT_NAME . ":{$signature}"
]
]);
$contents = json_decode($response->getBody());
dd($contents);
}
private function createRidiculouslyOverComplicatedSignature($verb, $contentType, $apiVersion, $ocpDate, $body)
{
$contentLength = mb_strlen(json_encode($body, JSON_NUMERIC_CHECK), '8bit');
$canonicalizedHeaders = "ocp-date:{$ocpDate}";
$canonicalizedResource = "/" . BATCH_ACCOUNT_NAME . "/pools\napi-version:{$apiVersion}";
$stringToSign = "{$verb}\n\n\n{$contentLength}\n\n{$contentType}\n\n\n\n\n\n\n{$canonicalizedHeaders}\n{$canonicalizedResource}";
echo utf8_encode($stringToSign);
return base64_encode(hash_hmac('sha256', utf8_encode($stringToSign), BATCH_ACCOUNT_KEY));
}
但是,我总是收到 403 错误:
"Server failed to authenticate the request. Make sure the value of
Authorization header is formed correctly including the signature"
由于复杂的设置和模糊的错误消息,我真的很难弄明白,where/why它失败了。尝试调整我能想到的每个选项,但没有。我在这里错过了什么?
更新:
我设法将批处理授权库从官方 python sdk 转换为 php。这是我想出的:
private function createPoolIfNotExist()
{
echo "-- creating batch pool --\n\n";
$credentials = new BatchSharedKeyCredentials(
BATCH_ACCOUNT_NAME,
BATCH_ACCOUNT_KEY,
BATCH_ENDPOINT,
);
$body = [
"id" => POOL_ID,
"vmSize" => POOL_VM_SIZE,
"targetDedicatedNodes" => 0,
"targetLowPriorityNodes" => 1,
];
$stack = HandlerStack::create(new CurlHandler());
$stack->push(new BatchAuthentication($credentials));
$client = new Client([
'handler' => $stack,
]);
$client->post(BATCH_ENDPOINT . "/pools?api-version=2021-06-01.14.0", [
'json' => $body
]);
dd("end");
}
class BatchAuthentication
{
public BatchSharedKeyCredentials $credentials;
public function __construct(BatchSharedKeyCredentials $credentials)
{
$this->credentials = $credentials;
}
public function __invoke(callable $handler)
{
return function (RequestInterface $request, array $options) use ($handler) {
$newRequest = $this->signHeader($request);
return $handler(
$newRequest,
$options
);
};
}
private function sign(string $stringToSign)
{
$key = $this->credentials->keyValue;
$stringToSign = utf8_encode($stringToSign);
$key = base64_decode($key);
$sign = hash_hmac(
'sha256',
$stringToSign,
$key,
true
);
$signature = utf8_decode(base64_encode($sign));
echo ($signature);
return $signature;
}
private function signHeader(RequestInterface $request)
{
// Set Headers
if ($request->getHeader("ocp-date") == null) {
$dateTime = Carbon::now('UTC')->toRfc7231String();
$request = $request->withAddedHeader("ocp-date", $dateTime);
}
echo ("\n ocp date: " . $request->getHeader("ocp-date")[0] . "\n");
$signature = $request->getMethod() . "\n";
$signature .= $this->headerValue($request, "Content-Encoding") . "\n";
$signature .= $this->headerValue($request, "Content-Language") . "\n";
// Special handle content length
$length = -1;
if ($request->getBody() != null) {
$length = $request->getBody()->getSize();
}
$signature .= ($length >= 0 ? $length : "") . "\n";
$signature .= $this->headerValue($request, "Content-MD5") . "\n";
// Special handle content type header
$contentType = "";
if ($request->getBody() != null && $request->getBody()->getSize() != 0) {
//here it differs. But official docs say like this:
$contentType = "application/json; odata=minimalmetadata; charset=utf-8";
}
$signature .= $contentType . "\n";
$signature .= $this->headerValue($request, "Date") . "\n";
$signature .= $this->headerValue($request, "If-Modified-Since") . "\n";
$signature .= $this->headerValue($request, "If-Match") . "\n";
$signature .= $this->headerValue($request, "If-None-Match") . "\n";
$signature .= $this->headerValue($request, "If-Unmodified-Since") . "\n";
$signature .= $this->headerValue($request, "Range") . "\n";
$customHeaders = array();
foreach ($request->getHeaders() as $key => $value) {
if (str_starts_with(strtolower($key), "ocp-")) {
array_push($customHeaders, strtolower($key));
}
}
sort($customHeaders);
foreach ($customHeaders as $canonicalHeader) {
$value = $request->getHeader($canonicalHeader)[0];
$value = str_replace('\n', ' ', $value);
$value = str_replace('\r', ' ', $value);
$value = preg_replace("/^[ ]+/", "", $value);
$signature .= $canonicalHeader . ":" . $value . "\n";
}
$signature .= "/" . strtolower($this->credentials->accountName) . "/"
. str_replace("/", "", $request->getUri()->getPath());
$query = $request->getUri()->getQuery();
if ($query != null) {
$queryComponents = array();
$pairs = explode("&", $query);
foreach ($pairs as $pair) {
$idx = strpos($pair, "=");
$key = strtolower(urldecode(mb_strcut($pair, 0, $idx, "UTF-8")));
$queryComponents[$key] = $key . ":" . urldecode(mb_strcut($pair, $idx + 1, strlen($pair), "UTF-8"));
}
foreach ($queryComponents as $key => $value) {
$signature .= "\n" . $value;
}
}
echo ("\nsignature:\n" . $signature . "\n");
$signedSignature = $this->sign($signature);
$authorization = "SharedKey " . $this->credentials->accountName . ":" . $signedSignature;
$request = $request->withAddedHeader("Authorization", $authorization);
echo "\n";
foreach ($request->getHeaders() as $key => $value) {
echo ($key . " : " . $value[0] . "\n");
}
return $request;
}
private function headerValue(RequestInterface $request, String $headerName): String
{
$headerValue = $request->getHeader($headerName);
if ($headerValue == null) {
return "";
}
return $headerValue[0];
}
}
class BatchSharedKeyCredentials
{
public string $accountName;
public string $keyValue;
public string $baseUrl;
public function __construct(string $accountName, string $keyValue, string $baseUrl)
{
$this->accountName = $accountName;
$this->keyValue = $keyValue;
$this->baseUrl = $baseUrl;
}
}
我 运行 进行了一些测试,用于在(有效的)python 示例和我的 php 脚本中使用“test-string”进行签名过程。签名是一样的,所以我的签名功能现在可以正常使用了!
我还比较了headers和要签名的字符串。他们是一样的!
然而在 php 它抛出 403 错误,告诉我
The MAC signature found in the HTTP request 'mySignatureCode' is not the same as any computed signature.
我花了一个星期才弄明白,如果你不指定,guzzle 会自动设置 content-type header。
我 post 我的整个脚本,以防其他人想做同样的事情 - 不需要太痛苦 - 现在应该可以正常工作了:
<?php
namespace App\Http\Middleware;
use GuzzleHttp\Client;
use GuzzleHttp\Handler\CurlHandler;
use GuzzleHttp\HandlerStack;
use Psr\Http\Message\RequestInterface;
use Illuminate\Support\Carbon;
class AzureBatchClient extends Client
{
public function __construct(array $config = [])
{
$stack = HandlerStack::create(new CurlHandler());
$stack->push(new BatchAuthentication(new BatchSharedKeyCredentials(
env("AZURE_BATCH_ACCOUNT"),env("AZURE_BATCH_KEY")
)));
$config['handler'] = $stack;
parent::__construct($config);
}
}
class BatchAuthentication
{
public BatchSharedKeyCredentials $credentials;
public function __construct(BatchSharedKeyCredentials $credentials)
{
$this->credentials = $credentials;
}
public function __invoke(callable $handler)
{
return function (RequestInterface $request, array $options) use ($handler) {
$newRequest = $this->signHeader($request);
return $handler(
$newRequest,
$options
);
};
}
private function sign(string $stringToSign)
{
$key = $this->credentials->keyValue;
$stringToSign = utf8_encode($stringToSign);
$key = base64_decode($key);
$sign = hash_hmac(
'sha256',
$stringToSign,
$key,
true
);
$signature = utf8_decode(base64_encode($sign));
//echo ($signature);
return $signature;
}
private function signHeader(RequestInterface $request)
{
// Set Headers
if ($request->getHeader("ocp-date") == null) {
$dateTime = Carbon::now('UTC')->toRfc7231String();
$request = $request->withAddedHeader("ocp-date", $dateTime);
}
//echo ("\n ocp date: " . $request->getHeader("ocp-date")[0] . "\n");
$signature = $request->getMethod() . "\n";
$signature .= $this->headerValue($request, "Content-Encoding") . "\n";
$signature .= $this->headerValue($request, "Content-Language") . "\n";
// Special handle content length
$length = -1;
if ($request->getBody() != null) {
$length = $request->getBody()->getSize();
}
$signature .= ($length > 0 ? $length : "") . "\n";
$signature .= $this->headerValue($request, "Content-MD5") . "\n";
// Special handle content type header
$contentType = "";
if ($request->getBody() != null && $request->getBody()->getSize() != 0) {
//here it differs. But official docs say like this:
$contentType = "application/json; odata=minimalmetadata; charset=utf-8";
}
$signature .= $contentType . "\n";
$signature .= $this->headerValue($request, "Date") . "\n";
$signature .= $this->headerValue($request, "If-Modified-Since") . "\n";
$signature .= $this->headerValue($request, "If-Match") . "\n";
$signature .= $this->headerValue($request, "If-None-Match") . "\n";
$signature .= $this->headerValue($request, "If-Unmodified-Since") . "\n";
$signature .= $this->headerValue($request, "Range") . "\n";
$customHeaders = array();
foreach ($request->getHeaders() as $key => $value) {
if (str_starts_with(strtolower($key), "ocp-")) {
array_push($customHeaders, strtolower($key));
}
}
sort($customHeaders);
foreach ($customHeaders as $canonicalHeader) {
$value = $request->getHeader($canonicalHeader)[0];
$value = str_replace('\n', ' ', $value);
$value = str_replace('\r', ' ', $value);
$value = preg_replace("/^[ ]+/", "", $value);
$signature .= $canonicalHeader . ":" . $value . "\n";
}
$path = substr_replace($request->getUri()->getPath(), "", 0, strlen("/"));
// echo $path;
$signature .= "/" . strtolower($this->credentials->accountName) . "/" . $path;
$query = $request->getUri()->getQuery();
if ($query != null) {
$queryComponents = array();
$pairs = explode("&", $query);
foreach ($pairs as $pair) {
$idx = strpos($pair, "=");
$key = strtolower(urldecode(mb_strcut($pair, 0, $idx, "UTF-8")));
$queryComponents[$key] = $key . ":" . urldecode(mb_strcut($pair, $idx + 1, strlen($pair), "UTF-8"));
}
foreach ($queryComponents as $key => $value) {
$signature .= "\n" . $value;
}
}
//echo ("\n\n" . str_replace("\n", "\n", $signature) . "\n\n");
$signedSignature = $this->sign($signature);
$authorization = "SharedKey " . $this->credentials->accountName . ":" . $signedSignature;
$request = $request->withAddedHeader("Authorization", $authorization);
/*
foreach ($request->getHeaders() as $key => $value) {
echo ($key . " : " . $value[0] . "\n");
}
*/
return $request;
}
private function headerValue(RequestInterface $request, String $headerName): String
{
$headerValue = $request->getHeader($headerName);
if ($headerValue == null) {
return "";
}
return $headerValue[0];
}
}
class BatchSharedKeyCredentials
{
public string $accountName;
public string $keyValue;
public function __construct(string $accountName, string $keyValue)
{
$this->accountName = $accountName;
$this->keyValue = $keyValue;
}
}
然后你像这样使用它 POST:
$client = new AzureBatchClient();
$client->post(BATCH_ENDPOINT . "/pools?api-version=" . API_VERISON, [
'json' => $body,
'headers' => [
"Content-Type" => "application/json; odata=minimalmetadata; charset=utf-8"
]
]);
像这样获取 GET 等:
$client = new AzureBatchClient();
$client->get(BATCH_ENDPOINT . "/jobs/{$jobId}?api-version=" . API_VERISON);
请确保您的 header 中的 content-type 与您的签名字符串匹配。
我正在尝试从我的 php 服务器对 azures 批处理 REST 接口进行身份验证。 根据文档 (https://docs.microsoft.com/en-us/rest/api/batchservice/authenticate-requests-to-the-azure-batch-service) 我想出了这个函数:
use GuzzleHttp\Client;
const BATCH_ACCOUNT_NAME = "myAccount";
const BATCH_ACCOUNT_KEY = "mySuperSecretKey";
const BATCH_ENDPOINT = "https://myAccount.theRegion.batch.azure.com";
// Pool and Job constants
const POOL_ID = "MyTestPool";
const POOL_VM_SIZE = "STANDARD_A1_v2";
private function createPoolIfNotExists()
{
echo "-- creating batch pool --\n\n";
$client = new Client();
$body = [
"id" => POOL_ID,
"vmSize" => POOL_VM_SIZE,
];
$contentType = "application/json;odata=minimalmetadata";
$apiVersion = "2021-06-01.14.0";
$ocpDate = Carbon::now('UTC')->toString("R");
$signature = $this->createRidiculouslyOverComplicatedSignature(
"POST",
$contentType,
$apiVersion,
$ocpDate,
$body
);
$response = $client->post(BATCH_ENDPOINT . "/pools?api-version={$apiVersion}", [
'json' => $body,
'headers' => [
"ocp-date" => $ocpDate,
"Authorization" => "SharedKey " . BATCH_ACCOUNT_NAME . ":{$signature}"
]
]);
$contents = json_decode($response->getBody());
dd($contents);
}
private function createRidiculouslyOverComplicatedSignature($verb, $contentType, $apiVersion, $ocpDate, $body)
{
$contentLength = mb_strlen(json_encode($body, JSON_NUMERIC_CHECK), '8bit');
$canonicalizedHeaders = "ocp-date:{$ocpDate}";
$canonicalizedResource = "/" . BATCH_ACCOUNT_NAME . "/pools\napi-version:{$apiVersion}";
$stringToSign = "{$verb}\n\n\n{$contentLength}\n\n{$contentType}\n\n\n\n\n\n\n{$canonicalizedHeaders}\n{$canonicalizedResource}";
echo utf8_encode($stringToSign);
return base64_encode(hash_hmac('sha256', utf8_encode($stringToSign), BATCH_ACCOUNT_KEY));
}
但是,我总是收到 403 错误:
"Server failed to authenticate the request. Make sure the value of Authorization header is formed correctly including the signature"
由于复杂的设置和模糊的错误消息,我真的很难弄明白,where/why它失败了。尝试调整我能想到的每个选项,但没有。我在这里错过了什么?
更新: 我设法将批处理授权库从官方 python sdk 转换为 php。这是我想出的:
private function createPoolIfNotExist()
{
echo "-- creating batch pool --\n\n";
$credentials = new BatchSharedKeyCredentials(
BATCH_ACCOUNT_NAME,
BATCH_ACCOUNT_KEY,
BATCH_ENDPOINT,
);
$body = [
"id" => POOL_ID,
"vmSize" => POOL_VM_SIZE,
"targetDedicatedNodes" => 0,
"targetLowPriorityNodes" => 1,
];
$stack = HandlerStack::create(new CurlHandler());
$stack->push(new BatchAuthentication($credentials));
$client = new Client([
'handler' => $stack,
]);
$client->post(BATCH_ENDPOINT . "/pools?api-version=2021-06-01.14.0", [
'json' => $body
]);
dd("end");
}
class BatchAuthentication
{
public BatchSharedKeyCredentials $credentials;
public function __construct(BatchSharedKeyCredentials $credentials)
{
$this->credentials = $credentials;
}
public function __invoke(callable $handler)
{
return function (RequestInterface $request, array $options) use ($handler) {
$newRequest = $this->signHeader($request);
return $handler(
$newRequest,
$options
);
};
}
private function sign(string $stringToSign)
{
$key = $this->credentials->keyValue;
$stringToSign = utf8_encode($stringToSign);
$key = base64_decode($key);
$sign = hash_hmac(
'sha256',
$stringToSign,
$key,
true
);
$signature = utf8_decode(base64_encode($sign));
echo ($signature);
return $signature;
}
private function signHeader(RequestInterface $request)
{
// Set Headers
if ($request->getHeader("ocp-date") == null) {
$dateTime = Carbon::now('UTC')->toRfc7231String();
$request = $request->withAddedHeader("ocp-date", $dateTime);
}
echo ("\n ocp date: " . $request->getHeader("ocp-date")[0] . "\n");
$signature = $request->getMethod() . "\n";
$signature .= $this->headerValue($request, "Content-Encoding") . "\n";
$signature .= $this->headerValue($request, "Content-Language") . "\n";
// Special handle content length
$length = -1;
if ($request->getBody() != null) {
$length = $request->getBody()->getSize();
}
$signature .= ($length >= 0 ? $length : "") . "\n";
$signature .= $this->headerValue($request, "Content-MD5") . "\n";
// Special handle content type header
$contentType = "";
if ($request->getBody() != null && $request->getBody()->getSize() != 0) {
//here it differs. But official docs say like this:
$contentType = "application/json; odata=minimalmetadata; charset=utf-8";
}
$signature .= $contentType . "\n";
$signature .= $this->headerValue($request, "Date") . "\n";
$signature .= $this->headerValue($request, "If-Modified-Since") . "\n";
$signature .= $this->headerValue($request, "If-Match") . "\n";
$signature .= $this->headerValue($request, "If-None-Match") . "\n";
$signature .= $this->headerValue($request, "If-Unmodified-Since") . "\n";
$signature .= $this->headerValue($request, "Range") . "\n";
$customHeaders = array();
foreach ($request->getHeaders() as $key => $value) {
if (str_starts_with(strtolower($key), "ocp-")) {
array_push($customHeaders, strtolower($key));
}
}
sort($customHeaders);
foreach ($customHeaders as $canonicalHeader) {
$value = $request->getHeader($canonicalHeader)[0];
$value = str_replace('\n', ' ', $value);
$value = str_replace('\r', ' ', $value);
$value = preg_replace("/^[ ]+/", "", $value);
$signature .= $canonicalHeader . ":" . $value . "\n";
}
$signature .= "/" . strtolower($this->credentials->accountName) . "/"
. str_replace("/", "", $request->getUri()->getPath());
$query = $request->getUri()->getQuery();
if ($query != null) {
$queryComponents = array();
$pairs = explode("&", $query);
foreach ($pairs as $pair) {
$idx = strpos($pair, "=");
$key = strtolower(urldecode(mb_strcut($pair, 0, $idx, "UTF-8")));
$queryComponents[$key] = $key . ":" . urldecode(mb_strcut($pair, $idx + 1, strlen($pair), "UTF-8"));
}
foreach ($queryComponents as $key => $value) {
$signature .= "\n" . $value;
}
}
echo ("\nsignature:\n" . $signature . "\n");
$signedSignature = $this->sign($signature);
$authorization = "SharedKey " . $this->credentials->accountName . ":" . $signedSignature;
$request = $request->withAddedHeader("Authorization", $authorization);
echo "\n";
foreach ($request->getHeaders() as $key => $value) {
echo ($key . " : " . $value[0] . "\n");
}
return $request;
}
private function headerValue(RequestInterface $request, String $headerName): String
{
$headerValue = $request->getHeader($headerName);
if ($headerValue == null) {
return "";
}
return $headerValue[0];
}
}
class BatchSharedKeyCredentials
{
public string $accountName;
public string $keyValue;
public string $baseUrl;
public function __construct(string $accountName, string $keyValue, string $baseUrl)
{
$this->accountName = $accountName;
$this->keyValue = $keyValue;
$this->baseUrl = $baseUrl;
}
}
我 运行 进行了一些测试,用于在(有效的)python 示例和我的 php 脚本中使用“test-string”进行签名过程。签名是一样的,所以我的签名功能现在可以正常使用了!
我还比较了headers和要签名的字符串。他们是一样的!
然而在 php 它抛出 403 错误,告诉我
The MAC signature found in the HTTP request 'mySignatureCode' is not the same as any computed signature.
我花了一个星期才弄明白,如果你不指定,guzzle 会自动设置 content-type header。
我 post 我的整个脚本,以防其他人想做同样的事情 - 不需要太痛苦 - 现在应该可以正常工作了:
<?php
namespace App\Http\Middleware;
use GuzzleHttp\Client;
use GuzzleHttp\Handler\CurlHandler;
use GuzzleHttp\HandlerStack;
use Psr\Http\Message\RequestInterface;
use Illuminate\Support\Carbon;
class AzureBatchClient extends Client
{
public function __construct(array $config = [])
{
$stack = HandlerStack::create(new CurlHandler());
$stack->push(new BatchAuthentication(new BatchSharedKeyCredentials(
env("AZURE_BATCH_ACCOUNT"),env("AZURE_BATCH_KEY")
)));
$config['handler'] = $stack;
parent::__construct($config);
}
}
class BatchAuthentication
{
public BatchSharedKeyCredentials $credentials;
public function __construct(BatchSharedKeyCredentials $credentials)
{
$this->credentials = $credentials;
}
public function __invoke(callable $handler)
{
return function (RequestInterface $request, array $options) use ($handler) {
$newRequest = $this->signHeader($request);
return $handler(
$newRequest,
$options
);
};
}
private function sign(string $stringToSign)
{
$key = $this->credentials->keyValue;
$stringToSign = utf8_encode($stringToSign);
$key = base64_decode($key);
$sign = hash_hmac(
'sha256',
$stringToSign,
$key,
true
);
$signature = utf8_decode(base64_encode($sign));
//echo ($signature);
return $signature;
}
private function signHeader(RequestInterface $request)
{
// Set Headers
if ($request->getHeader("ocp-date") == null) {
$dateTime = Carbon::now('UTC')->toRfc7231String();
$request = $request->withAddedHeader("ocp-date", $dateTime);
}
//echo ("\n ocp date: " . $request->getHeader("ocp-date")[0] . "\n");
$signature = $request->getMethod() . "\n";
$signature .= $this->headerValue($request, "Content-Encoding") . "\n";
$signature .= $this->headerValue($request, "Content-Language") . "\n";
// Special handle content length
$length = -1;
if ($request->getBody() != null) {
$length = $request->getBody()->getSize();
}
$signature .= ($length > 0 ? $length : "") . "\n";
$signature .= $this->headerValue($request, "Content-MD5") . "\n";
// Special handle content type header
$contentType = "";
if ($request->getBody() != null && $request->getBody()->getSize() != 0) {
//here it differs. But official docs say like this:
$contentType = "application/json; odata=minimalmetadata; charset=utf-8";
}
$signature .= $contentType . "\n";
$signature .= $this->headerValue($request, "Date") . "\n";
$signature .= $this->headerValue($request, "If-Modified-Since") . "\n";
$signature .= $this->headerValue($request, "If-Match") . "\n";
$signature .= $this->headerValue($request, "If-None-Match") . "\n";
$signature .= $this->headerValue($request, "If-Unmodified-Since") . "\n";
$signature .= $this->headerValue($request, "Range") . "\n";
$customHeaders = array();
foreach ($request->getHeaders() as $key => $value) {
if (str_starts_with(strtolower($key), "ocp-")) {
array_push($customHeaders, strtolower($key));
}
}
sort($customHeaders);
foreach ($customHeaders as $canonicalHeader) {
$value = $request->getHeader($canonicalHeader)[0];
$value = str_replace('\n', ' ', $value);
$value = str_replace('\r', ' ', $value);
$value = preg_replace("/^[ ]+/", "", $value);
$signature .= $canonicalHeader . ":" . $value . "\n";
}
$path = substr_replace($request->getUri()->getPath(), "", 0, strlen("/"));
// echo $path;
$signature .= "/" . strtolower($this->credentials->accountName) . "/" . $path;
$query = $request->getUri()->getQuery();
if ($query != null) {
$queryComponents = array();
$pairs = explode("&", $query);
foreach ($pairs as $pair) {
$idx = strpos($pair, "=");
$key = strtolower(urldecode(mb_strcut($pair, 0, $idx, "UTF-8")));
$queryComponents[$key] = $key . ":" . urldecode(mb_strcut($pair, $idx + 1, strlen($pair), "UTF-8"));
}
foreach ($queryComponents as $key => $value) {
$signature .= "\n" . $value;
}
}
//echo ("\n\n" . str_replace("\n", "\n", $signature) . "\n\n");
$signedSignature = $this->sign($signature);
$authorization = "SharedKey " . $this->credentials->accountName . ":" . $signedSignature;
$request = $request->withAddedHeader("Authorization", $authorization);
/*
foreach ($request->getHeaders() as $key => $value) {
echo ($key . " : " . $value[0] . "\n");
}
*/
return $request;
}
private function headerValue(RequestInterface $request, String $headerName): String
{
$headerValue = $request->getHeader($headerName);
if ($headerValue == null) {
return "";
}
return $headerValue[0];
}
}
class BatchSharedKeyCredentials
{
public string $accountName;
public string $keyValue;
public function __construct(string $accountName, string $keyValue)
{
$this->accountName = $accountName;
$this->keyValue = $keyValue;
}
}
然后你像这样使用它 POST:
$client = new AzureBatchClient();
$client->post(BATCH_ENDPOINT . "/pools?api-version=" . API_VERISON, [
'json' => $body,
'headers' => [
"Content-Type" => "application/json; odata=minimalmetadata; charset=utf-8"
]
]);
像这样获取 GET 等:
$client = new AzureBatchClient();
$client->get(BATCH_ENDPOINT . "/jobs/{$jobId}?api-version=" . API_VERISON);
请确保您的 header 中的 content-type 与您的签名字符串匹配。