From 0225cce6c96d09d9de18cd3e3b7e881463a0c463 Mon Sep 17 00:00:00 2001 From: lewzylu <327874225@qq.com> Date: Wed, 16 Sep 2020 18:11:48 +0800 Subject: [PATCH] Feature signature params (#161) * update signature * del old file Co-authored-by: lewzylu --- src/Qcloud/Cos/Client.php | 5 +- .../Cos/CommandToRequestTransformer.php | 191 ++++++++++-------- src/Qcloud/Cos/Common.php | 61 +++--- src/Qcloud/Cos/CosTransformer.php | 162 --------------- src/Qcloud/Cos/Signature.php | 103 +++++++--- 5 files changed, 216 insertions(+), 306 deletions(-) delete mode 100644 src/Qcloud/Cos/CosTransformer.php diff --git a/src/Qcloud/Cos/Client.php b/src/Qcloud/Cos/Client.php index f0bd21b8..24ec6dfb 100644 --- a/src/Qcloud/Cos/Client.php +++ b/src/Qcloud/Cos/Client.php @@ -14,11 +14,8 @@ use GuzzleHttp\Command\Guzzle\Deserializer; use GuzzleHttp\Command\CommandInterface; use GuzzleHttp\Command\Exception\CommandException; -use GuzzleHttp\Exception\RequestException; use GuzzleHttp\Middleware; use GuzzleHttp\Psr7; -use GuzzleHttp\Pool; - class Client extends GuzzleClient { const VERSION = '2.0.9'; @@ -46,7 +43,7 @@ public function __construct($cosConfig) { $this->cosConfig['connect_timeout'] = isset($cosConfig['connect_timeout']) ? $cosConfig['connect_timeout'] : 3600; $this->cosConfig['ip'] = isset($cosConfig['ip']) ? $cosConfig['ip'] : null; $this->cosConfig['port'] = isset($cosConfig['port']) ? $cosConfig['port'] : null; - $this->cosConfig['endpoint'] = isset($cosConfig['endpoint']) ? $cosConfig['endpoint'] : 'myqcloud.com'; + $this->cosConfig['endpoint'] = isset($cosConfig['endpoint']) ? $cosConfig['endpoint'] : null; $this->cosConfig['domain'] = isset($cosConfig['domain']) ? $cosConfig['domain'] : null; $this->cosConfig['proxy'] = isset($cosConfig['proxy']) ? $cosConfig['proxy'] : null; $this->cosConfig['retry'] = isset($cosConfig['retry']) ? $cosConfig['retry'] : 1; diff --git a/src/Qcloud/Cos/CommandToRequestTransformer.php b/src/Qcloud/Cos/CommandToRequestTransformer.php index 7d2bb46c..850f78a3 100644 --- a/src/Qcloud/Cos/CommandToRequestTransformer.php +++ b/src/Qcloud/Cos/CommandToRequestTransformer.php @@ -2,162 +2,175 @@ namespace Qcloud\Cos; -use Guzzle\Service\Description\Parameter; -use Guzzle\Service\Description\ServiceDescription; -use GuzzleHttp\HandlerStack; use Psr\Http\Message\RequestInterface; -use Psr\Http\Message\ResponseInterface; -use Qcloud\Cos\Signature; -use GuzzleHttp\Command\Guzzle\Description; -use GuzzleHttp\Command\Guzzle\GuzzleClient; use GuzzleHttp\Command\CommandInterface; -use GuzzleHttp\Exception\RequestException; -use GuzzleHttp\Middleware; -use GuzzleHttp\Psr7; use GuzzleHttp\Psr7\Uri; use InvalidArgumentException; - class CommandToRequestTransformer { private $config; private $operation; - public function __construct($config ,$operation) { + public function __construct( $config, $operation ) { $this->config = $config; $this->operation = $operation; } // format bucket style - public function bucketStyleTransformer(CommandInterface $command, RequestInterface $request) { + + public function bucketStyleTransformer( CommandInterface $command, RequestInterface $request ) { $action = $command->getName(); if ($action == 'ListBuckets') { - return $request->withUri(new Uri($this->config['schema']."://service.cos.myqcloud.com/")); + $uri = "service.cos.myqcloud.com"; + + if ($this->config['endpoint'] != null) { + $uri = $this->config['endpoint']; + } + if ($this->config['domain'] != null) { + $uri = $this->config['domain']; + } + if ($this->config['ip'] != null) { + $uri = $this->config['ip']; + if ($this->config['port'] != null) { + $uri = $this->config['ip'] . ":" . $this->config['port']; + } + } + return $request->withUri(new Uri($this->config['schema']."://". $uri. "/")); } $operation = $this->operation; $bucketname = $command['Bucket']; $appId = $this->config['appId']; - if ($appId != null && endWith($bucketname, '-'.$appId) == False) - { + if ( $appId != null && endWith( $bucketname, '-'.$appId ) == False ) { $bucketname = $bucketname.'-'.$appId; } $command['Bucket'] = $bucketname; - $path = ''; + $path = ''; + $http_method = $operation['httpMethod']; $uri = $operation['uri']; - + // Hoststyle is used by default // Pathstyle - if ($this->config['pathStyle'] != true) { - if (isset($operation['parameters']['Bucket']) && $command->hasParam('Bucket')) { - $uri = str_replace("{Bucket}", '', $uri); - } - if (isset($operation['parameters']['Key']) && $command->hasParam('Key')) { - $uri = str_replace("{/Key*}", encodeKey($command['Key']), $uri); + if ( $this->config['pathStyle'] != true ) { + if ( isset( $operation['parameters']['Bucket'] ) && $command->hasParam( 'Bucket' ) ) { + $uri = str_replace( '{Bucket}', '', $uri ); + } + + if ( isset( $operation['parameters']['Key'] ) && $command->hasParam( 'Key' ) ) { + $uri = str_replace( '{/Key*}', encodeKey( $command['Key'] ), $uri ); } } + if ($this->config['endpoint'] == null) { + $this->config['endpoint'] = "myqcloud.com"; + } $origin_host = $bucketname. '.cos.' . $this->config['region'] . '.' . $this->config['endpoint']; // domain - if ($this->config['domain'] != null) { + if ( $this->config['domain'] != null ) { $origin_host = $this->config['domain']; } $host = $origin_host; - if ($this->config['ip'] != null) { + if ( $this->config['ip'] != null ) { $host = $this->config['ip']; - if ($this->config['port'] != null) { - $host = $this->config['ip'] . ":" . $this->config['port']; + if ( $this->config['port'] != null ) { + $host = $this->config['ip'] . ':' . $this->config['port']; } } - $path = $this->config['schema'].'://'. $host . $uri; - $uri = new Uri($path); + $uri = new Uri( $path ); $query = $request->getUri()->getQuery(); - if ($uri->getQuery() != $query && $uri->getQuery() != "") { - $query = $uri->getQuery() . "&" . $request->getUri()->getQuery(); + if ( $uri->getQuery() != $query && $uri->getQuery() != '' ) { + $query = $uri->getQuery() . '&' . $request->getUri()->getQuery(); } - $uri = $uri->withQuery($query); - $request = $request->withUri($uri); - $request = $request->withHeader('Host', $origin_host); + $uri = $uri->withQuery( $query ); + $request = $request->withUri( $uri ); + $request = $request->withHeader( 'Host', $origin_host ); return $request; } // format upload body - public function uploadBodyTransformer(CommandInterface $command, $request, $bodyParameter = 'Body', $sourceParameter = 'SourceFile') { - + + public function uploadBodyTransformer( CommandInterface $command, $request, $bodyParameter = 'Body', $sourceParameter = 'SourceFile' ) { + $operation = $this->operation; - if (!isset($operation['parameters']['Body'])) { + if ( !isset( $operation['parameters']['Body'] ) ) { return $request; } - $source = isset($command[$sourceParameter]) ? $command[$sourceParameter] : null; - $body = isset($command[$bodyParameter]) ? $command[$bodyParameter] : null; + $source = isset( $command[$sourceParameter] ) ? $command[$sourceParameter] : null; + $body = isset( $command[$bodyParameter] ) ? $command[$bodyParameter] : null; // If a file path is passed in then get the file handle - if (is_string($source) && file_exists($source)) { - $body = fopen($source, 'rb'); + if ( is_string( $source ) && file_exists( $source ) ) { + $body = fopen( $source, 'rb' ); } // Prepare the body parameter and remove the source file parameter - if (null !== $body) { + if ( null !== $body ) { return $request; } else { throw new InvalidArgumentException( - "You must specify a non-null value for the {$bodyParameter} or {$sourceParameter} parameters."); + "You must specify a non-null value for the {$bodyParameter} or {$sourceParameter} parameters." ); + } } - } - // update md5 - public function md5Transformer(CommandInterface $command, $request) { - $operation = $this->operation; - if (isset($operation['data']['contentMd5'])) { - $request = $this->addMd5($request); + // update md5 + + public function md5Transformer( CommandInterface $command, $request ) { + $operation = $this->operation; + if ( isset( $operation['data']['contentMd5'] ) ) { + $request = $this->addMd5( $request ); + } + if ( isset( $operation['parameters']['ContentMD5'] ) && + isset( $command['ContentMD5'] ) ) { + $value = $command['ContentMD5']; + if ( $value === true ) { + $request = $this->addMd5( $request ); + } + } + + return $request; } - if (isset($operation['parameters']['ContentMD5']) && - isset($command['ContentMD5'])) { - $value = $command['ContentMD5']; - if ($value === true) { - $request = $this->addMd5($request); + + // add meta + + public function metadataTransformer( CommandInterface $command, $request ) { + $operation = $this->operation; + if ( isset( $command['Metadata'] ) ) { + $meta = $command['Metadata']; + foreach ( $meta as $key => $value ) { + $request = $request->withHeader( 'x-cos-meta-' . $key, $value ); + } } + $request = headersMap( $command, $request ); + + return $request; } - return $request; - } + // count md5 - // add meta - public function metadataTransformer(CommandInterface $command, $request) { - $operation = $this->operation; - if (isset($command['Metadata'])) { - $meta = $command['Metadata']; - foreach ($meta as $key => $value) { - $request = $request->withHeader('x-cos-meta-' . $key, $value); + private function addMd5( $request ) { + $body = $request->getBody(); + if ( $body && $body->getSize() > 0 ) { + $md5 = base64_encode( md5( $body, true ) ); + return $request->withHeader( 'Content-MD5', $md5 ); } + return $request; } - $request = headersMap($command, $request); - return $request; - } - // count md5 - private function addMd5($request) { - $body = $request->getBody(); - if ($body && $body->getSize() > 0) { - $md5 = base64_encode(md5($body, true)); - return $request->withHeader('Content-MD5', $md5); + // inventoryId + + public function specialParamTransformer( CommandInterface $command, $request ) { + $action = $command->getName(); + if ( $action == 'PutBucketInventory' ) { + $id = $command['Id']; + $uri = $request->getUri(); + $query = $uri->getQuery(); + $uri = $uri->withQuery( $query . '&Id='.$id ); + return $request->withUri( $uri ); + } + return $request; } - return $request; - } - // inventoryId - public function specialParamTransformer(CommandInterface $command, $request) { - $action = $command->getName(); - if ($action == 'PutBucketInventory') { - $id = $command['Id']; - $uri = $request->getUri(); - $query = $uri->getQuery(); - $uri = $uri->withQuery($query . "&Id=".$id); - return $request->withUri($uri); + public function __destruct() { } - return $request; - } - public function __destruct() { } - -} diff --git a/src/Qcloud/Cos/Common.php b/src/Qcloud/Cos/Common.php index e962ed47..aab806a3 100644 --- a/src/Qcloud/Cos/Common.php +++ b/src/Qcloud/Cos/Common.php @@ -2,46 +2,53 @@ namespace Qcloud\Cos; -function region_map($region) { - $regionmap = array('cn-east'=>'ap-shanghai', - 'cn-south'=>'ap-guangzhou', - 'cn-north'=>'ap-beijing-1', - 'cn-south-2'=>'ap-guangzhou-2', - 'cn-southwest'=>'ap-chengdu', - 'sg'=>'ap-singapore', - 'tj'=>'ap-beijing-1', - 'bj'=>'ap-beijing', - 'sh'=>'ap-shanghai', - 'gz'=>'ap-guangzhou', - 'cd'=>'ap-chengdu', - 'sgp'=>'ap-singapore'); - if (array_key_exists($region, $regionmap)) { +function region_map( $region ) { + $regionmap = array( 'cn-east'=>'ap-shanghai', + 'cn-south'=>'ap-guangzhou', + 'cn-north'=>'ap-beijing-1', + 'cn-south-2'=>'ap-guangzhou-2', + 'cn-southwest'=>'ap-chengdu', + 'sg'=>'ap-singapore', + 'tj'=>'ap-beijing-1', + 'bj'=>'ap-beijing', + 'sh'=>'ap-shanghai', + 'gz'=>'ap-guangzhou', + 'cd'=>'ap-chengdu', + 'sgp'=>'ap-singapore' ); + if ( array_key_exists( $region, $regionmap ) ) { return $regionmap[$region]; } return $region; } -function encodeKey($key) { - return str_replace('%2F', '/', rawurlencode($key)); +function encodeKey( $key ) { + return str_replace( '%2F', '/', rawurlencode( $key ) ); } -function endWith($haystack, $needle) { - $length = strlen($needle); - if($length == 0) - { +function endWith( $haystack, $needle ) { + $length = strlen( $needle ); + if ( $length == 0 ) { return true; } - return (substr($haystack, -$length) === $needle); + return ( substr( $haystack, -$length ) === $needle ); } -function headersMap($command, $request) { +function startWith( $haystack, $needle ) { + $length = strlen( $needle ); + if ( $length == 0 ) { + return true; + } + return ( substr( $haystack, $length ) === $needle ); +} + +function headersMap( $command, $request ) { $headermap = array( - 'TransferEncoding'=>'Transfer-Encoding', - 'ChannelId'=>'x-cos-channel-id' + 'TransferEncoding'=>'Transfer-Encoding', + 'ChannelId'=>'x-cos-channel-id' ); - foreach ($headermap as $key => $value) { - if (isset($command[$key])) { - $request = $request->withHeader($value, $command[$key]); + foreach ( $headermap as $key => $value ) { + if ( isset( $command[$key] ) ) { + $request = $request->withHeader( $value, $command[$key] ); } } return $request; diff --git a/src/Qcloud/Cos/CosTransformer.php b/src/Qcloud/Cos/CosTransformer.php deleted file mode 100644 index c3f6e1b2..00000000 --- a/src/Qcloud/Cos/CosTransformer.php +++ /dev/null @@ -1,162 +0,0 @@ -config = $config; - $this->operation = $operation; - } - - // format bucket style - public function bucketStyleTransformer(CommandInterface $command, RequestInterface $request) { - $action = $command->getName(); - if ($action == 'ListBuckets') { - return $request->withUri(new Uri($this->config['schema']."://service.cos.myqcloud.com/")); - } - $operation = $this->operation; - $bucketname = $command['Bucket']; - - $appId = $this->config['appId']; - if ($appId != null && endWith($bucketname, '-'.$appId) == False) - { - $bucketname = $bucketname.'-'.$appId; - } - $command['Bucket'] = $bucketname; - $path = ''; - $http_method = $operation['httpMethod']; - $uri = $operation['uri']; - - // Hoststyle is used by default - // Pathstyle - if ($this->config['pathStyle'] != true) { - if (isset($operation['parameters']['Bucket']) && $command->hasParam('Bucket')) { - $uri = str_replace("{Bucket}", '', $uri); - } - if (isset($operation['parameters']['Key']) && $command->hasParam('Key')) { - $uri = str_replace("{/Key*}", encodeKey($command['Key']), $uri); - } - } - $origin_host = $bucketname. '.cos.' . $this->config['region'] . '.' . $this->config['endpoint']; - // domain - if ($this->config['domain'] != null) { - $origin_host = $this->config['domain']; - } - $host = $origin_host; - if ($this->config['ip'] != null) { - $host = $this->config['ip']; - if ($this->config['port'] != null) { - $host = $this->config['ip'] . ":" . $this->config['port']; - } - } - - - $path = $this->config['schema'].'://'. $host . $uri; - $uri = new Uri($path); - $query = $request->getUri()->getQuery(); - if ($uri->getQuery() != $query && $uri->getQuery() != "") { - $query = $uri->getQuery() . "&" . $request->getUri()->getQuery(); - } - $uri = $uri->withQuery($query); - $request = $request->withUri($uri); - $request = $request->withHeader('Host', $origin_host); - return $request; - } - - // format upload body - public function uploadBodyTransformer(CommandInterface $command, $request, $bodyParameter = 'Body', $sourceParameter = 'SourceFile') { - - $operation = $this->operation; - if (!isset($operation['parameters']['Body'])) { - return $request; - } - $source = isset($command[$sourceParameter]) ? $command[$sourceParameter] : null; - $body = isset($command[$bodyParameter]) ? $command[$bodyParameter] : null; - // If a file path is passed in then get the file handle - if (is_string($source) && file_exists($source)) { - $body = fopen($source, 'rb'); - } - // Prepare the body parameter and remove the source file parameter - if (null !== $body) { - return $request; - } else { - throw new InvalidArgumentException( - "You must specify a non-null value for the {$bodyParameter} or {$sourceParameter} parameters."); - } - } - - // update md5 - public function md5Transformer(CommandInterface $command, $request) { - $operation = $this->operation; - if (isset($operation['data']['contentMd5'])) { - $request = $this->addMd5($request); - } - if (isset($operation['parameters']['ContentMD5']) && - isset($command['ContentMD5'])) { - $value = $command['ContentMD5']; - if ($value === true) { - $request = $this->addMd5($request); - } - } - - return $request; - } - - // add meta - public function metadataTransformer(CommandInterface $command, $request) { - $operation = $this->operation; - if (isset($command['Metadata'])) { - $meta = $command['Metadata']; - foreach ($meta as $key => $value) { - $request = $request->withHeader('x-cos-meta-' . $key, $value); - } - } - return $request; - } - - // count md5 - private function addMd5($request) { - $body = $request->getBody(); - if ($body && $body->getSize() > 0) { - $md5 = base64_encode(md5($body, true)); - return $request->withHeader('Content-MD5', $md5); - } - return $request; - } - - // inventoryId - public function specialParamTransformer(CommandInterface $command, $request) { - $action = $command->getName(); - if ($action == 'PutBucketInventory') { - $id = $command['Id']; - $uri = $request->getUri(); - $query = $uri->getQuery(); - $uri = $uri->withQuery($query . "&Id=".$id); - return $request->withUri($uri); - } - return $request; - } - - public function __destruct() { - } - -} diff --git a/src/Qcloud/Cos/Signature.php b/src/Qcloud/Cos/Signature.php index 72e3ecbb..ab1b06e9 100644 --- a/src/Qcloud/Cos/Signature.php +++ b/src/Qcloud/Cos/Signature.php @@ -5,44 +5,99 @@ use Psr\Http\Message\RequestInterface; class Signature { - private $accessKey; // string: access key. - private $secretKey; // string: secret key. - public function __construct($accessKey, $secretKey, $token=null) { + private $accessKey; + // string: access key. + private $secretKey; + // string: secret key. + + public function __construct( $accessKey, $secretKey, $token = null ) { $this->accessKey = $accessKey; $this->secretKey = $secretKey; $this->token = $token; - date_default_timezone_set("PRC"); + $this->signHeader = [ + 'host', + 'content-type', + 'content-md5', + 'content-disposition', + 'content-encoding', + 'content-length', + 'transfer-encoding', + 'range', + ]; + date_default_timezone_set( 'PRC' ); } + public function __destruct() { } - public function signRequest(RequestInterface $request) { - $authorization = $this->createAuthorization($request); - return $request->withHeader('Authorization', $authorization); + + public function needCheckHeader( $header ) { + if ( startWith( $header, 'x-cos-' ) ) { + return true; + } + if ( in_array( $header, $this->signHeader ) ) { + return true; + } + return false; + } + + public function signRequest( RequestInterface $request ) { + $authorization = $this->createAuthorization( $request ); + return $request->withHeader( 'Authorization', $authorization ); } - public function createAuthorization(RequestInterface $request, $expires = "+30 minutes") { - if (is_null($expires)) { - $expires = "+30 minutes"; + + public function createAuthorization( RequestInterface $request, $expires = '+30 minutes' ) { + if ( is_null( $expires ) ) { + $expires = '+30 minutes'; + } + $signTime = ( string )( time() - 60 ) . ';' . ( string )( strtotime( $expires ) ); + $urlParamListArray = []; + foreach ( explode( '&', $request->getUri()->getQuery() ) as $query ) { + if (!empty($query)) { + $tmpquery = explode( '=', $query ); + $key = strtolower( urlencode( $tmpquery[0] ) ); + if (count($tmpquery) >= 2) { + $value = urlencode( $tmpquery[1] ); + } else { + $value = ""; + } + $urlParamListArray[$key] = $key. '='. $value; + } } - $signTime = (string)(time() - 60) . ';' . (string)(strtotime($expires)); - $httpString = strtolower($request->getMethod()) . "\n" . urldecode($request->getUri()->getPath()) . - "\n\nhost=" . $request->getHeader("Host")[0]. "\n"; - $sha1edHttpString = sha1($httpString); + ksort($urlParamListArray); + $urlParamList = join(';', array_keys($urlParamListArray)); + $httpParameters = join('&', array_values($urlParamListArray)); + + $headerListArray = []; + foreach ( $request->getHeaders() as $key => $value ) { + $key = strtolower( urlencode( $key ) ); + $value = urlencode( $value[0] ); + if ( $this->needCheckHeader( $key ) ) { + $headerListArray[$key] = $key. '='. $value; + } + } + ksort($headerListArray); + $headerList = join(';', array_keys($headerListArray)); + $httpHeaders = join('&', array_values($headerListArray)); + $httpString = strtolower( $request->getMethod() ) . "\n" . urldecode( $request->getUri()->getPath() ) . "\n" . $httpParameters. + "\n". $httpHeaders. "\n"; + $sha1edHttpString = sha1( $httpString ); $stringToSign = "sha1\n$signTime\n$sha1edHttpString\n"; - $signKey = hash_hmac('sha1', $signTime, $this->secretKey); - $signature = hash_hmac('sha1', $stringToSign, $signKey); + $signKey = hash_hmac( 'sha1', $signTime, $this->secretKey ); + $signature = hash_hmac( 'sha1', $stringToSign, $signKey ); $authorization = 'q-sign-algorithm=sha1&q-ak='. $this->accessKey . - "&q-sign-time=$signTime&q-key-time=$signTime&q-header-list=host&q-url-param-list=&" . - "q-signature=$signature"; + "&q-sign-time=$signTime&q-key-time=$signTime&q-header-list=$headerList&q-url-param-list=$urlParamList&" . + "q-signature=$signature"; return $authorization; } - public function createPresignedUrl(RequestInterface $request, $expires = "+30 minutes") { - $authorization = $this->createAuthorization($request, $expires); + + public function createPresignedUrl( RequestInterface $request, $expires = '+30 minutes' ) { + $authorization = $this->createAuthorization( $request, $expires ); $uri = $request->getUri(); - $query = "sign=".urlencode($authorization); - if ($this->token != null) { - $query = $query."&x-cos-security-token=".$this->token; + $query = 'sign='.urlencode( $authorization ); + if ( $this->token != null ) { + $query = $query.'&x-cos-security-token='.$this->token; } - $uri = $uri->withQuery($query); + $uri = $uri->withQuery( $query ); return $uri; } }