OAuth 1.0 Генерация подписи с PHP

Я хочу создать подпись для моего настраиваемого заголовка, чтобы получить токены доступа от Magento. «oauth_signature_method» – «HMAC-SHA1». Я подготовил базовую строку, но не знаю, как создать подпись. Должен ли я использовать «hash_hmac ($ algo, $ data, $ key)»? Если это так, каков должен быть ключ? Любая помощь будет оценена по достоинству.

$consumerSecret = "abcdef"; ... $nonce = generateNonce(); $time = time(); ... $base = "POST&http%3A%2F%2Fmydomain.com%2Fmagento%2Foauth%2Ftoken&". "oauth_consumer_key%3Dc67eee22b81ac3237cc501aa58fdc236%26". "oauth_nonce%3D".$nonce."%26". "oauth_signature_method%3DHMAC-SHA1%26". "oauth_timestamp%3D".$time."%26". "oauth_version%3D1.0%26". "oauth_token%3D".$oauthToken."%26". "oauth_verifier%3D".$oauthVerifier; 

Вы можете найти документацию OAuth 1.0 здесь , точнее для ваших проблемных (под) разделов

  • 9.2. HMAC-SHA1
  • Приложение A.5.1. Генерирование базовой строки подписи
  • Приложение A.5.2. Вычисление значения подписи

Согласно документации, строка подписи основы формируется как: HTTP_METHOD&urlencode(BASE_URL_OF_RESOURCE)&urlencode(PARAMETERS_ORDERED_AND_NORMALIZED_AND_WITHOUT_OAUTHSIGNATURE)

Ключ, используемый для hasing:
urlencode(CONSUMER_SECRET)&urlencode(OAUTH_TOKEN_SECRET) – когда у вас секретный токен
urlencode(CONSUMER_SECRET)& – когда у вас нет секретности токена (в самом начале при запросе «токена запроса»)

Окончательная подпись должна быть base64_encode(hash_hmac('SHA1', $signatureBaseString, $key, 1));

Я приведу пример сценария, чтобы вы могли использовать / test / посмотреть, как он работает:

  <?php // testMagentoRestApi.php file error_reporting(E_ALL); ini_set('display_errors', 'On'); session_start(); /** * @author Bogdan Constantinescu <{first3LettersFromFirstname}_{first3LettersFromLastname}{at}yahoo{dot}com> * @license The MIT License (http://opensource.org/licenses/MIT) */ class MagentoRestApi { /** * @var const int Curl connect timeout. */ const TIMEOUT = 20; /** * @var string Application 's key. */ private $appKey; /** * @var string Application 's secret. */ private $appSecret; /** * @var resource The curl resource. */ protected $curl; /** * @var string Base Magento url. */ protected $baseMagentoUrl; /** * @var string Consumer 's callback url. */ protected $callbackUrl; /** * Constructor; initializes stuffs... * @param string $strBaseMagentoUrl Magento 's base url. * @param string $strAppKey API application key. * @param string $strAppSecret API application secret. * @param string $strCallbackUrl Consumer's callback url. * @throws Exception If params are not ok / curl could not be initialized. */ public function __construct($strBaseMagentoUrl, $strAppKey, $strAppSecret, $strCallbackUrl) { // check params if(!filter_var($strBaseMagentoUrl, FILTER_VALIDATE_URL)) { throw new Exception('Invalid param base magento url.'); } if (!is_string($strAppKey) || !mb_strlen($strAppKey)) { throw new Exception('Invalid param app key.'); } if (!is_string($strAppSecret) || !mb_strlen($strAppSecret)) { throw new Exception('Invalid param app secret.'); } if(!filter_var($strCallbackUrl, FILTER_VALIDATE_URL)) { throw new Exception('Invalid param callback url.'); } $this->baseMagentoUrl = trim($strBaseMagentoUrl, '/'); $this->callbackUrl = $strCallbackUrl; $this->appKey = $strAppKey; $this->appSecret = $strAppSecret; if (!extension_loaded('curl')) { throw new Exception('cURL extension is not enabled.'); } $this->curl = curl_init(); if (false === $this->curl) { throw new Exception('cURL could not be initialized.'); } } /** * Makes api call. * @param string $strUrl Api call url. * @param string $strMethod Request method (POST|GET|DELETE|PUT...) * @param array $arrHeaders Optional request headers. * @param string $strPostData Request body. * @param boolean $blnSuprimeResponseHeader Whether to suprime response 's headers or not. * @return string Api call response. * @throws Exception If smth went wrong. */ protected function makeApiCall( $strUrl, $strMethod = 'GET', array $arrHeaders = array(), $strPostData = '', $blnSuprimeResponseHeader = false, &$intStatus = null ) { curl_setopt($this->curl, CURLOPT_URL, $strUrl); curl_setopt($this->curl, CURLOPT_HTTPHEADER, $arrHeaders); curl_setopt($this->curl, CURLOPT_TIMEOUT, self::TIMEOUT); curl_setopt($this->curl, CURLOPT_CONNECTTIMEOUT, self::TIMEOUT); curl_setopt($this->curl, CURLOPT_MAXREDIRS, 3); curl_setopt($this->curl, CURLOPT_RETURNTRANSFER, 1); curl_setopt($this->curl, CURLOPT_FOLLOWLOCATION, 1); curl_setopt($this->curl, CURLOPT_CUSTOMREQUEST, $strMethod); curl_setopt($this->curl, CURLOPT_POSTFIELDS, $strPostData); curl_setopt($this->curl, CURLOPT_HEADER, !$blnSuprimeResponseHeader); curl_setopt($this->curl, CURLOPT_USERAGENT, 'Magento REST API Client'); curl_setopt($this->curl, CURLOPT_SSL_VERIFYPEER, 0); $mxdResponse = curl_exec($this->curl); if (false === $mxdResponse) { throw new Exception(curl_error($this->curl), curl_errno($this->curl)); } $intStatus = curl_getinfo($this->curl, CURLINFO_HTTP_CODE); // var_dump($mxdResponse); return $mxdResponse; } /** * Retrieve request token. * @return array With keys 'oauth_token' & 'oauth_token_secret' * @throws Exception If request did not succeed/smth went bad */ public function getRequestToken() { $returnValue = array(); // define params that will be used either in Authorization header, or as url query params, excluding 'oauth_signature' $params = array( 'oauth_callback' => $this->callbackUrl, 'oauth_consumer_key' => $this->appKey, 'oauth_nonce' => uniqid(mt_rand(1, 1000)), 'oauth_signature_method' => 'HMAC-SHA1', 'oauth_timestamp' => time(), 'oauth_version' => '1.0', ); // define HTTP method $method = 'POST'; // this is the url to get Request Token according to Magento doc $url = $this->baseMagentoUrl . '/oauth/initiate?oauth_callback=' . urlencode($params['oauth_callback']); // start making the signature ksort($params); // @see Zend_Oauth_Signature_SignatureAbstract::_toByteValueOrderedQueryString() for more accurate sorting, including array params $sortedParamsByKeyEncodedForm = array(); foreach ($params as $key => $value) { $sortedParamsByKeyEncodedForm[] = rawurlencode($key) . '=' . rawurlencode($value); } $strParams = implode('&', $sortedParamsByKeyEncodedForm); $signatureData = strtoupper($method) // HTTP method (POST/GET/PUT/...) . '&' . rawurlencode($this->baseMagentoUrl . '/oauth/initiate') // base resource url - without port & query params & anchors, @see how Zend extracts it in Zend_Oauth_Signature_SignatureAbstract::normaliseBaseSignatureUrl() . '&' . rawurlencode($strParams); $key = rawurlencode($this->appSecret) . '&'; // on later requests, when you also have a oauth_token_secret from this request append it to $key ( eq: $key = rawurlencode($this->appSecret) . '&' . rawurlencode($someOauthTokenSecret); ) $signature = base64_encode(hash_hmac('SHA1', $signatureData, $key, 1)); // end making signature $responseStatusCode = 0; $response = $this->makeApiCall( $url, $method, array( 'Authorization: OAuth ' . 'oauth_callback="' . rawurlencode($params['oauth_callback']) . '",' . 'oauth_consumer_key="' . $params['oauth_consumer_key'] . '",' . 'oauth_nonce="' . $params['oauth_nonce'] . '",' . 'oauth_signature_method="' . $params['oauth_signature_method'] . '",' . 'oauth_signature="' . rawurlencode($signature) . '",' . 'oauth_timestamp="' . $params['oauth_timestamp'] . '",' . 'oauth_version="' . $params['oauth_version'] . '"' ), '', true, $responseStatusCode ); if (200 == $responseStatusCode) { parse_str($response, $returnValue); } else { throw new Exception('Response HTTP code != 200, but ' . $responseStatusCode); } return $returnValue; } /** * Retrieve access token. * @param string $strOauthToken token from "Request Token". * @param string $strOauthTokenSecret token secret from "Request Token". * @param string $strOauthVerifier verifier returened after user authorization. * @return array With keys 'oauth_token' & 'oauth_token_secret' * @throws Exception If request did not succeed/smth went bad */ public function getAccessToken($strOauthToken, $strOauthTokenSecret, $strOauthVerifier) { $returnValue = array(); // define params that will be used either in Authorization header, or as url query params, excluding 'oauth_signature' $params = array( 'oauth_consumer_key' => $this->appKey, 'oauth_nonce' => uniqid(mt_rand(1, 1000)), 'oauth_signature_method' => 'HMAC-SHA1', 'oauth_timestamp' => time(), 'oauth_version' => '1.0', 'oauth_token' => $strOauthToken, 'oauth_verifier' => $strOauthVerifier, ); // define HTTP method $method = 'POST'; // this is the url to get Request Token according to Magento doc $url = $this->baseMagentoUrl . '/oauth/token'; // start making the signature ksort($params); // @see Zend_Oauth_Signature_SignatureAbstract::_toByteValueOrderedQueryString() for more accurate sorting, including array params $sortedParamsByKeyEncodedForm = array(); foreach ($params as $key => $value) { $sortedParamsByKeyEncodedForm[] = rawurlencode($key) . '=' . rawurlencode($value); } $strParams = implode('&', $sortedParamsByKeyEncodedForm); $signatureData = strtoupper($method) // HTTP method (POST/GET/PUT/...) . '&' . rawurlencode($url) // base resource url - without port & query params & anchors, @see how Zend extracts it in Zend_Oauth_Signature_SignatureAbstract::normaliseBaseSignatureUrl() . '&' . rawurlencode($strParams); $key = rawurlencode($this->appSecret) . '&' . rawurlencode($strOauthTokenSecret); $signature = base64_encode(hash_hmac('SHA1', $signatureData, $key, 1)); // end making signature $responseStatusCode = 0; $response = $this->makeApiCall( $url, $method, array( 'Authorization: OAuth ' . 'oauth_consumer_key="' . $params['oauth_consumer_key'] . '",' . 'oauth_nonce="' . $params['oauth_nonce'] . '",' . 'oauth_signature_method="' . $params['oauth_signature_method'] . '",' . 'oauth_signature="' . rawurlencode($signature) . '",' . 'oauth_timestamp="' . $params['oauth_timestamp'] . '",' . 'oauth_version="' . $params['oauth_version'] . '",' . 'oauth_token="' . rawurlencode($params['oauth_token']) . '",' . 'oauth_verifier="' . rawurlencode($params['oauth_verifier']) . '"' ), '', true, $responseStatusCode ); if (200 == $responseStatusCode) { parse_str($response, $returnValue); } else { throw new Exception('Response HTTP code != 200, but ' . $responseStatusCode); } return $returnValue; } /** * Destructor. Free resources. */ public function __destruct() { if (is_resource($this->curl)) { curl_close($this->curl); } } } // change the following variables accordingly to your needs $callbackUrl = 'http://localhost/testMagentoRestApi.php'; $baseMagentoUrl = 'http://www.magento.dev/CE_1.9.1.0'; $consumerKey = 'e93a5396269851aaddaabd86999bafcb'; $consumerSecret = 'bfb0c10cf75f0b66f71186d806616807'; $client = new MagentoRestApi($baseMagentoUrl, $consumerKey, $consumerSecret, $callbackUrl); if (!isset($_SESSION['request_token']) && !isset($_GET['oauth_token'])) { // retrieve "Request Token" and Authorize user $_SESSION['request_token'] = $client->getRequestToken(); header('Location: ' . $baseMagentoUrl . '/oauth/authorize?oauth_token=' . urlencode($_SESSION['request_token']['oauth_token'])); exit; } elseif (isset($_SESSION['request_token']) && isset($_GET['oauth_token']) && isset($_GET['oauth_verifier'])) { // user authorized and redirected here, now request access token (step E from http://img.ruphp.com/php/diagram.png) $_SESSION['access_token'] = $client->getAccessToken($_GET['oauth_token'], $_SESSION['request_token']['oauth_token_secret'], $_GET['oauth_verifier']); var_dump($_SESSION['access_token']); } 

Также вы можете сделать обратную разработку в magento. Например, при запросе «токена запроса» поток:

 Mage_Oauth_InitiateController::indexAction() Mage_Oauth_Model_Server::initiateToken() Mage_Oauth_Model_Server::_processRequest() Mage_Oauth_Model_Server::_validateSignature() Zend_Oauth_Http_Utility::sign() Zend_Oauth_Signature_Hmac::sign() Zend_Oauth_Signature_SignatureAbstract::_getBaseSignatureString() 

Вокруг последних 3 классов происходит все волшебство. Вы можете проверить их.