YopClient3.php 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682
  1. <?php
  2. namespace app\common\modules\yop\sdk;
  3. use app\common\modules\yop\sdk\YopRequest;
  4. use app\common\modules\yop\sdk\YopResponse;
  5. use app\common\modules\yop\sdk\Util\YopSignUtils;
  6. use app\common\modules\yop\sdk\Util\HttpRequest;
  7. use app\common\modules\yop\sdk\Util\BlowfishEncrypter;
  8. use app\common\modules\yop\sdk\Util\AESEncrypter;
  9. use app\common\modules\yop\sdk\Util\StringUtils;
  10. use app\common\modules\yop\sdk\Util\HttpUtils;
  11. use app\common\modules\yop\sdk\Util\Base64Url;
  12. class YopClient3
  13. {
  14. public function __construct()
  15. {
  16. }
  17. /**
  18. * @param $methodOrUri
  19. * @param $YopRequest
  20. * @param $encode_data
  21. * @return array
  22. */
  23. public static function SignRsaParameter($methodOrUri, $YopRequest)
  24. {
  25. $appKey = $YopRequest->{$YopRequest->Config->APP_KEY};
  26. if (empty($appKey)) {
  27. $appKey = $YopRequest->Config->CUSTOMER_NO;
  28. $YopRequest->removeParam($YopRequest->Config->APP_KEY);
  29. }
  30. if (empty($appKey)) {
  31. error_log("appKey 与 customerNo 不能同时为空");
  32. }
  33. date_default_timezone_set('PRC');
  34. $dataTime = new \DateTime();
  35. //$timestamp=$dataTime->format('c'); // Returns ISO8601 in proper format
  36. $timestamp = $dataTime->format(\DateTime::ISO8601); // Works the same since const ISO8601 = "Y-m-d\TH:i:sO"
  37. //$timestamp = "2016-02-25T08:57:48Z";
  38. //$requestId = YopClient3::uuid();//Returns like ‘1225c695-cfb8-4ebb-aaaa-80da344e8352′
  39. $requestId="123456";
  40. $headers = array();
  41. $headers['x-yop-request-id'] = $requestId;
  42. $headers['x-yop-date'] = $timestamp;
  43. $protocolVersion = "yop-auth-v2";
  44. $EXPIRED_SECONDS = "1800";
  45. $authString = $protocolVersion . "/" . $appKey . "/" . $timestamp . "/" . $EXPIRED_SECONDS;
  46. $headersToSignSet = array();
  47. array_push($headersToSignSet, "x-yop-request-id");
  48. array_push($headersToSignSet, "x-yop-date");
  49. $appKey = $YopRequest->{$YopRequest->Config->APP_KEY};
  50. if (StringUtils::isBlank($YopRequest->Config->CUSTOMER_NO)) {
  51. $headers['x-yop-appkey'] = $appKey;
  52. array_push($headersToSignSet, "x-yop-appkey");
  53. } else {
  54. $headers['x-yop-customerid'] = $appKey;
  55. array_push($headersToSignSet, "x-yop-customerid");
  56. }
  57. // Formatting the URL with signing protocol.
  58. $canonicalURI = HttpUtils::getCanonicalURIPath($methodOrUri);
  59. // Formatting the query string with signing protocol.
  60. $canonicalQueryString = YopClient3::getCanonicalQueryString($YopRequest, true);
  61. // Sorted the headers should be signed from the request.
  62. $headersToSign = YopClient3::getHeadersToSign($headers, $headersToSignSet);
  63. // Formatting the headers from the request based on signing protocol.
  64. $canonicalHeader = YopClient3::getCanonicalHeaders($headersToSign);
  65. $signedHeaders = "";
  66. if ($headersToSignSet != null) {
  67. foreach ($headersToSign as $key => $value) {
  68. $signedHeaders .= strlen($signedHeaders) == 0 ? "" : ";";
  69. $signedHeaders .= $key;
  70. }
  71. $signedHeaders = strtolower($signedHeaders);
  72. }
  73. $canonicalRequest = $authString . "\n" . "POST" . "\n" . $canonicalURI . "\n" . $canonicalQueryString . "\n" . $canonicalHeader;
  74. // Signing the canonical request using key with sha-256 algorithm.
  75. if (empty($YopRequest->secretKey)) {
  76. error_log("secretKey must be specified");
  77. }
  78. extension_loaded('openssl') or die('php需要openssl扩展支持');
  79. $private_key = $YopRequest->secretKey;
  80. $private_key = "-----BEGIN RSA PRIVATE KEY-----\n" .
  81. wordwrap($private_key, 64, "\n", true) .
  82. "\n-----END RSA PRIVATE KEY-----";
  83. /* 提取私钥 */
  84. $privateKey = openssl_pkey_get_private($private_key);
  85. ($privateKey) or die('密钥不可用');
  86. $signToBase64 = "";
  87. openssl_sign($canonicalRequest, $encode_data, $privateKey, "SHA256");
  88. openssl_free_key($privateKey);
  89. $signToBase64 = Base64Url::encode($encode_data);
  90. $signToBase64 .= '$SHA256';
  91. $headers['Authorization'] = "YOP-RSA2048-SHA256 " . $protocolVersion . "/" . $appKey . "/" . $timestamp . "/" . $EXPIRED_SECONDS . "/" . $signedHeaders . "/" . $signToBase64;
  92. return $headers;
  93. }
  94. public function __set($name, $value)
  95. {
  96. // TODO: Implement __set() method.
  97. $this->$name = $value;
  98. }
  99. public function __get($name)
  100. {
  101. // TODO: Implement __get() method.
  102. return $this->$name;
  103. }
  104. static public function get($methodOrUri, $YopRequest){
  105. $content = YopClient3::getForString($methodOrUri, $YopRequest);
  106. $response = YopClient3::unmarshal($content);
  107. YopClient3::handleRsaResult($YopRequest, $response, $content);
  108. return $response;
  109. }
  110. static public function getForString($methodOrUri, $YopRequest){
  111. $serverUrl = YopClient3::richRequest($methodOrUri, $YopRequest);
  112. YopClient::signAndEncrypt($YopRequest);
  113. $YopRequest->absoluteURL = $serverUrl;
  114. $YopRequest->encoding();
  115. $serverUrl .= (strpos($serverUrl,'?') === false ?'?':'&') . $YopRequest->toQueryString();
  116. $response = YopClient3::getRestTemplate($serverUrl,$YopRequest,"GET");
  117. return $response;
  118. }
  119. public static function post($methodOrUri, $YopRequest)
  120. {
  121. $content = YopClient3::postString($methodOrUri, $YopRequest);
  122. $response = YopClient3::unmarshal($content);
  123. YopClient3::handleRsaResult($YopRequest, $response, $content);
  124. return $response;
  125. }
  126. /**
  127. * @param $methodOrUri
  128. * @param $YopRequest
  129. * @return type
  130. */
  131. public static function postString($methodOrUri, $YopRequest)
  132. {
  133. $serverUrl = YopClient3::richRequest($methodOrUri, $YopRequest);
  134. $YopRequest->absoluteURL = $serverUrl;
  135. $headers = self::SignRsaParameter($methodOrUri, $YopRequest);
  136. $response = YopClient3::getRestTemplate($serverUrl, $YopRequest, "POST", $headers);
  137. // echo $response;
  138. return $response;
  139. }
  140. /**
  141. * @param $YopRequest
  142. * @param $forSignature
  143. * @return string
  144. */
  145. public static function getCanonicalQueryString($YopRequest, $forSignature)
  146. {
  147. $ArrayList = array();
  148. $StrQuery = "";
  149. foreach ($YopRequest->paramMap as $k => $v) {
  150. if ($forSignature && strcasecmp($k, "Authorization") == 0) {
  151. continue;
  152. }
  153. array_push($ArrayList, $k . "=" . rawurlencode($v));
  154. }
  155. sort($ArrayList);
  156. foreach ($ArrayList as $kv) {
  157. $StrQuery .= strlen($StrQuery) == 0 ? "" : "&";
  158. $StrQuery .= $kv;
  159. }
  160. return $StrQuery;
  161. }
  162. /**
  163. * @param $headers
  164. * @param $headersToSign
  165. * @return arry
  166. */
  167. public static function getHeadersToSign($headers, $headersToSign)
  168. {
  169. $ret = array();
  170. if ($headersToSign != null) {
  171. $tempSet = array();
  172. foreach ($headersToSign as $header) {
  173. array_push($tempSet, strtolower(trim($header)));
  174. }
  175. $headersToSign = $tempSet;
  176. }
  177. foreach ($headers as $key => $value) {
  178. if ($value != null && !empty($value)) {
  179. if (($headersToSign == null && isDefaultHeaderToSign($key)) || ($headersToSign != null && in_array(strtolower($key), $headersToSign) && $key != "Authorization")) {
  180. $ret[$key] = $value;
  181. }
  182. }
  183. }
  184. ksort($ret);
  185. return $ret;
  186. }
  187. /**
  188. * @param $header
  189. * @return bool
  190. */
  191. public static function isDefaultHeaderToSign($header)
  192. {
  193. $header = strtolower(trim($header));
  194. $defaultHeadersToSign = array();
  195. array_push($defaultHeadersToSign, "host");
  196. array_push($defaultHeadersToSign, "content-length");
  197. array_push($defaultHeadersToSign, "content-type");
  198. array_push($defaultHeadersToSign, "content-md5");
  199. return strpos($header, "x-yop-") == 0 || in_array($defaultHeadersToSign, $header);
  200. }
  201. /**
  202. * @param $headers
  203. * @return string
  204. */
  205. public static function getCanonicalHeaders($headers)
  206. {
  207. if (empty($headers)) {
  208. return "";
  209. }
  210. $headerStrings = array();
  211. foreach ($headers as $key => $value) {
  212. if ($key == null) {
  213. continue;
  214. }
  215. if ($value == null) {
  216. $value = "";
  217. }
  218. $key = HttpUtils::normalize(strtolower(trim($key)));
  219. $value = HttpUtils::normalize(trim($value));
  220. array_push($headerStrings, $key . ':' . $value);
  221. }
  222. sort($headerStrings);
  223. $StrQuery = "";
  224. foreach ($headerStrings as $kv) {
  225. $StrQuery .= strlen($StrQuery) == 0 ? "" : "\n";
  226. $StrQuery .= $kv;
  227. }
  228. return $StrQuery;
  229. }
  230. /**
  231. * @param $methodOrUri
  232. * @param $YopRequest
  233. * @return YopResponse
  234. */
  235. static public function upload($methodOrUri, $YopRequest)
  236. {
  237. $content = YopClient3::uploadForString($methodOrUri, $YopRequest);
  238. $content = json_encode($content);
  239. $response = YopClient3::unmarshal($content);
  240. YopClient3::handleResult($YopRequest, $response, $content);
  241. return $response;
  242. }
  243. static public function uploadForString($methodOrUri, $YopRequest)
  244. {
  245. $serverUrl = YopClient3::richRequest($methodOrUri, $YopRequest);
  246. //$alternate = file_get_contents($YopRequest->getParam("_file"));
  247. //YopClient3::signAndEncrypt($YopRequest);
  248. $strTemp = $YopRequest->getParam("_file");
  249. $YopRequest->removeParam("_file");
  250. $headers = self::SignRsaParameter($methodOrUri, $YopRequest);
  251. $YopRequest->addParam("_file",$strTemp);
  252. //$YopRequest->addParam("_file",str_replace('file:','@',$strTemp));PUT
  253. // Create a CURLFile object
  254. //$cfile = curl_file_create($file);
  255. //echo $YopRequest->getParam("_file");
  256. $YopRequest->absoluteURL = $serverUrl;
  257. $response = YopClient3::getRestTemplate($serverUrl, $YopRequest, "PUT",$headers);
  258. return $response;
  259. }
  260. public static function unmarshal($content)
  261. {
  262. $jsoncontent= json_decode($content,true);
  263. /*
  264. *
  265. {
  266. "state" : "FAILURE",
  267. "ts" : 1469523373843,
  268. "error" : {
  269. "code" : "U000001",
  270. "message" : "会员不存在"
  271. }
  272. * */
  273. $YopResponse = new YopResponse();
  274. if (!empty($jsoncontent['state'])) {
  275. $YopResponse->state = $jsoncontent['state'];
  276. }
  277. if (!empty($jsoncontent['error'])) {
  278. if (is_array($jsoncontent['error'])) {
  279. foreach ($jsoncontent['error'] as $k => $v) {
  280. if (!is_array($v)) {
  281. $YopResponse->error .= (empty($YopResponse->error) ? '' : ',') . '"' . $k . '" : "' . $v . '"';
  282. } else {
  283. $YopResponse->error .= (empty($YopResponse->error) ? '' : ',') . '"' . $k . '" : "' . json_encode($v, JSON_UNESCAPED_UNICODE) . '"';
  284. foreach ($v as $vk => $vv) {
  285. }
  286. }
  287. }
  288. } else {
  289. $YopResponse->error = $jsoncontent['error'];
  290. }
  291. }
  292. if (!empty($jsoncontent['result'])) {
  293. $YopResponse->result = $jsoncontent['result'];
  294. }
  295. if (!empty($jsoncontent['ts'])) {
  296. $YopResponse->ts = $jsoncontent['ts'];
  297. }
  298. if (!empty($jsoncontent['sign'])) {
  299. $YopResponse->sign = $jsoncontent['sign'];
  300. }
  301. if (!empty($jsoncontent['stringResult'])) {
  302. $YopResponse->stringResult = $jsoncontent['stringResult'];
  303. }
  304. if (!empty($jsoncontent['format'])) {
  305. $YopResponse->format = $jsoncontent['format'];
  306. }
  307. if (!empty($jsoncontent['validSign'])) {
  308. $YopResponse->validSign = $jsoncontent['validSign'];
  309. }
  310. return $YopResponse;
  311. }
  312. public static function getRestTemplate($serverUrl, $YopRequest, $method, $headers)
  313. {
  314. $YopRequest->encoding();
  315. if ($method == "GET") {
  316. return HttpRequest::curl_request($serverUrl, '', $YopRequest->Config->connectTimeout, true);
  317. } elseif ($method == "PUT") {
  318. //$YopRequest->addParam("_file", $YopRequest->ImagePath);
  319. return HttpRequest::curl_request($serverUrl, $YopRequest->paramMap, $YopRequest->Config->connectTimeout, true, true,$headers);
  320. }
  321. return HttpRequest::curl_request($serverUrl, $YopRequest->paramMap, $YopRequest->Config->connectTimeout, false, false, $headers);
  322. }
  323. static public function signAndEncrypt($YopRequest)
  324. {
  325. if (empty($YopRequest->method)) {
  326. error_log("method must be specified");
  327. }
  328. if (empty($YopRequest->secretKey)) {
  329. error_log("secretKey must be specified");
  330. }
  331. $appKey = $YopRequest->{$YopRequest->Config->APP_KEY};
  332. if (empty($appKey)) {
  333. $appKey = $YopRequest->Config->CUSTOMER_NO;
  334. $YopRequest->removeParam($YopRequest->Config->APP_KEY);
  335. }
  336. if (empty($appKey)) {
  337. error_log("appKey 与 customerNo 不能同时为空");
  338. }
  339. $signValue = YopSignUtils::sign($YopRequest->paramMap, $YopRequest->ignoreSignParams, $YopRequest->secretKey, $YopRequest->signAlg);
  340. $YopRequest->addParam($YopRequest->Config->SIGN, $signValue);
  341. if ($YopRequest->isRest) {
  342. $YopRequest->removeParam($YopRequest->Config->METHOD);
  343. $YopRequest->removeParam($YopRequest->Config->VERSION);
  344. }
  345. if ($YopRequest->encrypt) {
  346. YopClient::encrypt($YopRequest);
  347. }
  348. }
  349. static public function encrypt($YopRequest)
  350. {
  351. $builder = $YopRequest->paramMap;
  352. foreach ($builder as $k => $v) {
  353. if ($YopRequest->Config->ispublicedKey($k)) {
  354. unset($builder[$k]);
  355. } else {
  356. $YopRequest->removeParam($k);
  357. }
  358. }
  359. if (!empty($builder)) {
  360. $encryptBody = "";
  361. foreach ($builder as $k => $v) {
  362. $encryptBody .= strlen($encryptBody) == 0 ? "" : "&";
  363. $encryptBody .= $k . "=" . urlencode($v);
  364. }
  365. }
  366. if (empty($encryptBody)) {
  367. $YopRequest->addParam($YopRequest->Config->ENCRYPT, true);
  368. } else {
  369. if (!empty($YopRequest->{$YopRequest->Config->APP_KEY})) {
  370. $encrypt = AESEncrypter::encode($encryptBody, $YopRequest->secretKey);
  371. $YopRequest->addParam($YopRequest->Config->ENCRYPT, $encrypt);
  372. } else {
  373. $encrypt = BlowfishEncrypter::encode($encryptBody, $YopRequest->secretKey);
  374. $YopRequest->addParam($YopRequest->Config->ENCRYPT, $encrypt);
  375. }
  376. }
  377. }
  378. static public function decrypt($YopRequest, $strResult)
  379. {
  380. if (!empty($strResult) && $YopRequest->{$YopRequest->Config->ENCRYPT}) {
  381. if (!empty($YopRequest->{$YopRequest->Config->APP_KEY})) {
  382. $strResult = AESEncrypter::decode($strResult, $YopRequest->secretKey);
  383. } else {
  384. $strResult = BlowfishEncrypter::decode($strResult, $YopRequest->secretKey);
  385. }
  386. }
  387. return $strResult;
  388. }
  389. static public function richRequest($methodOrUri, $YopRequest)
  390. {
  391. if (strpos($methodOrUri, $YopRequest->Config->serverRoot)) {
  392. $methodOrUri = substr($methodOrUri, strlen($YopRequest->Config->serverRoot) + 1);
  393. }
  394. $isRest = (strpos($methodOrUri, "/rest/") == 0) ? true : false;
  395. $YopRequest->isRest = $isRest;
  396. $serverUrl = $YopRequest->serverRoot;
  397. if ($isRest) {
  398. $serverUrl .= $methodOrUri;
  399. preg_match('@/rest/v([^/]+)/@i', $methodOrUri, $version);
  400. if (!empty($version)) {
  401. $version = $version[1];
  402. if (!empty($version)) {
  403. $YopRequest->setVersion($version);
  404. }
  405. }
  406. } else {
  407. $serverUrl .= "/command?" . $YopRequest->Config->METHOD . "=" . $methodOrUri;
  408. }
  409. $YopRequest->setMethod($methodOrUri);
  410. return $serverUrl;
  411. }
  412. public static function handleResult($YopRequest, $YopResponse, $content)
  413. {
  414. $YopResponse->format = $YopRequest->format;
  415. $ziped = '';
  416. if (strtoupper($YopResponse->state) == 'SUCCESS') {
  417. $strResult = self::getBizResult($content, $YopRequest->format);
  418. if (!empty($ziped) && $YopResponse->error == '') {
  419. if ($YopRequest->encrypt) {
  420. $decryptResult = self::decrypt($YopRequest, trim($ziped));
  421. $YopResponse->stringResult = $decryptResult;
  422. $YopResponse->result = $decryptResult;
  423. } else {
  424. $YopResponse->stringResult = $strResult;
  425. }
  426. }
  427. }
  428. if ($YopRequest->signRet && !empty($YopRequest->sign)) {
  429. $signStr = $YopResponse->state . $ziped . $YopResponse->ts;
  430. $YopResponse->validSign = YopSignUtils::isValidResult($signStr, $YopRequest->secretKey, $YopRequest->signAlg, $YopResponse->sign);
  431. } else {
  432. $YopResponse->validSign = true;
  433. }
  434. }
  435. public static function handleRsaResult($YopRequest, $YopResponse, $content)
  436. {
  437. $YopResponse->format = $YopRequest->format;
  438. $ziped = '';
  439. if (strtoupper($YopResponse->state) == 'SUCCESS') {
  440. $strResult =YopClient3::getBizResult($content, $YopRequest->format);
  441. $ziped =$strResult;
  442. if (!empty($ziped) && empty($YopResponse->error)) {
  443. $YopResponse->stringResult = $strResult;
  444. }
  445. }
  446. $YopResponse->validSign= YopClient3::isValidRsaResult($ziped, $YopResponse->sign,$YopRequest->yopPublicKey);
  447. }
  448. /**
  449. * 对业务结果签名进行校验
  450. */
  451. public static function isValidRsaResult($result, $sign,$public_key)
  452. {
  453. $sb = "";
  454. if ($result == null || empty($result)) {
  455. $sb = "";
  456. } else {
  457. $sb .= trim($result);
  458. }
  459. $public_key = "-----BEGIN PUBLIC KEY-----\n" .
  460. wordwrap($public_key, 64, "\n", true) .
  461. "\n-----END PUBLIC KEY-----";
  462. $pu_key = openssl_pkey_get_public($public_key);
  463. $sb= preg_replace("/[\s]{2,}/","",$sb);
  464. $sb= str_replace(PHP_EOL,"",$sb);
  465. $sb= str_replace(" ","",$sb);
  466. $res = openssl_verify($sb,Base64Url::decode(substr($sign,0,-7)), $pu_key,"SHA256"); //验证
  467. openssl_free_key($pu_key);
  468. if ($res == 1) {
  469. return true;
  470. } else {
  471. return false;
  472. }
  473. }
  474. private static function getBizResult($content, $format)
  475. {
  476. if (empty($format)) {
  477. return $content;
  478. }
  479. switch ($format) {
  480. case 'json':
  481. //preg_match('@"result" :(.+),"ts"@i', $content, $jsonStr);
  482. $result = strstr($content, '"result"');
  483. $length = strlen('"result"');
  484. $result = substr($result, $length+3);
  485. $result = substr($result,0,strrpos($result,'"ts"'));
  486. $result = substr($result,0,strlen($result)-4);
  487. return $result;
  488. default:
  489. //preg_match('@</state>(.+)<ts>@i', $content, $xmlStr);
  490. $result = strstr($content, '"</state>"');
  491. $length = strlen('</state>');
  492. $result = substr($result, $length+4);
  493. $result = substr($result,0,strrpos($result,'"ts"'));
  494. $result = substr($result, 0, -2);
  495. return $result;
  496. }
  497. }
  498. private static function uuid($prefix = '')
  499. {
  500. $chars = md5(uniqid(mt_rand(), true));
  501. $uuid = substr($chars, 0, 8) . '-';
  502. $uuid .= substr($chars, 8, 4) . '-';
  503. $uuid .= substr($chars, 12, 4) . '-';
  504. $uuid .= substr($chars, 16, 4) . '-';
  505. $uuid .= substr($chars, 20, 12);
  506. return $prefix . $uuid;
  507. }
  508. }