weibo.php 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388
  1. <?php
  2. namespace app\components\OpenAuth\core;
  3. use app\common\helpers\Session;
  4. use \Exception;
  5. use Yii;
  6. require_once 'weiboclient.php';
  7. class Weibo extends WeiboOAuth {
  8. function __construct($access_token = NULL, $refresh_token = NULL) {
  9. parent::__construct($access_token, $refresh_token);
  10. }
  11. function verify() {
  12. if (isset($_SESSION['weibo_token']) && $_SESSION['weibo_token'] && isset($_SESSION['weibo_token']['uid']) && !isset($_SESSION['weibo_token']['error'])) {
  13. return true;
  14. } else {
  15. return false;
  16. }
  17. }
  18. }
  19. class WeiboOAuth {
  20. public $client_id;
  21. public $client_secret;
  22. public $access_token;
  23. public $refresh_token;
  24. public $http_code;
  25. public $url;
  26. public $host = "https://api.weibo.com/2/";
  27. public $timeout = 30;
  28. public $connecttimeout = 30;
  29. public $ssl_verifypeer = FALSE;
  30. public $format = 'json';
  31. public $decode_json = TRUE;
  32. public $http_info;
  33. public $useragent = 'Weibo Oauth2.0';
  34. public $debug = FALSE;
  35. public static $boundary = '';
  36. function accessTokenURL() {
  37. return 'https://api.weibo.com/oauth2/access_token';
  38. }
  39. function authorizeURL() {
  40. return 'https://api.weibo.com/oauth2/authorize';
  41. }
  42. function __construct($access_token = NULL, $refresh_token = NULL) {
  43. $this->client_id = WB_APPID;
  44. $this->client_secret = WB_APPSECRET;
  45. $this->access_token = $access_token;
  46. $this->refresh_token = $refresh_token;
  47. }
  48. /**
  49. * authorize接口
  50. *
  51. * 对应API:{@link http://open.weibo.com/wiki/Oauth2/authorize Oauth2/authorize}
  52. *
  53. * @param string $url 授权后的回调地址,站外应用需与回调地址一致,站内应用需要填写canvas page的地址
  54. * @param string $response_type 支持的值包括 code 和token 默认值为code
  55. * @param string $state 用于保持请求和回调的状态。在回调时,会在Query Parameter中回传该参数
  56. * @param string $display 授权页面类型 可选范围:
  57. * - default 默认授权页面
  58. * - mobile 支持html5的手机
  59. * - wap1.2 wap1.2页面
  60. * - wap2.0 wap2.0页面
  61. * - apponweibo 站内应用专用,站内应用不传display参数,并且response_type为token时,默认使用改display.授权后不会返回access_token,只是输出js刷新站内应用父框架
  62. * @param bool $forcelogin 是否强制用户重新登录,true:是,false:否。默认false。
  63. * @param string $language 授权页语言,缺省为中文简体版,en为英文版。英文版测试中,开发者任何意见可反馈至 @微博API
  64. * @return array
  65. */
  66. function getAuthorizeURL($url, $response_type = 'code', $state = NULL, $display = NULL, $forcelogin = NULL, $language = NULL) {
  67. $params = array();
  68. $params['client_id'] = $this->client_id;
  69. $params['redirect_uri'] = $url;
  70. $params['response_type'] = $response_type;
  71. $params['state'] = $state;
  72. $params['display'] = $display;
  73. $params['forcelogin'] = $forcelogin;
  74. $params['language'] = $language;
  75. return $this->authorizeURL() . "?" . http_build_query($params);
  76. }
  77. /**
  78. * access_token接口
  79. *
  80. * 对应API:{@link http://open.weibo.com/wiki/OAuth2/access_token OAuth2/access_token}
  81. *
  82. * @param string $type 请求的类型,可以为:code, password, token
  83. * @param array $keys 其他参数:
  84. * - 当$type为code时: array('code'=>..., 'redirect_uri'=>...)
  85. * - 当$type为password时: array('username'=>..., 'password'=>...)
  86. * - 当$type为token时: array('refresh_token'=>...)
  87. * @return array
  88. */
  89. function getAccessToken($type = 'code', $keys = array()) {
  90. $params = array();
  91. $params['client_id'] = $this->client_id;
  92. $params['client_secret'] = $this->client_secret;
  93. if ($type === 'token') {
  94. $params['grant_type'] = 'refresh_token';
  95. $params['refresh_token'] = $keys['refresh_token'];
  96. } elseif ($type === 'code') {
  97. $params['grant_type'] = 'authorization_code';
  98. $params['code'] = $keys['code'];
  99. $params['redirect_uri'] = $keys['redirect_uri'];
  100. } elseif ($type === 'password') {
  101. $params['grant_type'] = 'password';
  102. $params['username'] = $keys['username'];
  103. $params['password'] = $keys['password'];
  104. } else {
  105. throw new Exception("wrong auth type");
  106. }
  107. $response = $this->oAuthRequest($this->accessTokenURL(), 'POST', $params);
  108. $token = json_decode($response, true);
  109. if (is_array($token) && !isset($token['error'])) {
  110. $this->access_token = $token['access_token'];
  111. $this->refresh_token = isset($token['refresh_token']) ? $token['refresh_token'] : '';
  112. } else {
  113. throw new Exception("get access token failed." . $token['error']);
  114. }
  115. return $token;
  116. }
  117. /**
  118. * 解析 signed_request
  119. *
  120. * @param string $signed_request 应用框架在加载iframe时会通过向Canvas URL post的参数signed_request
  121. *
  122. * @return array
  123. */
  124. function parseSignedRequest($signed_request) {
  125. list($encoded_sig, $payload) = explode('.', $signed_request, 2);
  126. $sig = self::base64decode($encoded_sig);
  127. $data = json_decode(self::base64decode($payload), true);
  128. if (strtoupper($data['algorithm']) !== 'HMAC-SHA256')
  129. return '-1';
  130. $expected_sig = hash_hmac('sha256', $payload, $this->client_secret, true);
  131. return ($sig !== $expected_sig) ? '-2' : $data;
  132. }
  133. /**
  134. * @ignore
  135. */
  136. function base64decode($str) {
  137. return base64_decode(strtr($str . str_repeat('=', (4 - strlen($str) % 4)), '-_', '+/'));
  138. }
  139. /**
  140. * 读取jssdk授权信息,用于和jssdk的同步登录
  141. *
  142. * @return array 成功返回array('access_token'=>'value', 'refresh_token'=>'value'); 失败返回false
  143. */
  144. function getTokenFromJSSDK() {
  145. $key = "weibojs_" . $this->client_id;
  146. if (isset($_COOKIE[$key]) && $cookie = $_COOKIE[$key]) {
  147. parse_str($cookie, $token);
  148. if (isset($token['access_token']) && isset($token['refresh_token'])) {
  149. $this->access_token = $token['access_token'];
  150. $this->refresh_token = $token['refresh_token'];
  151. return $token;
  152. } else {
  153. return false;
  154. }
  155. } else {
  156. return false;
  157. }
  158. }
  159. /**
  160. * 从数组中读取access_token和refresh_token
  161. * 常用于从Session或Cookie中读取token,或通过Session/Cookie中是否存有token判断登录状态。
  162. *
  163. * @param array $arr 存有access_token和secret_token的数组
  164. * @return array 成功返回array('access_token'=>'value', 'refresh_token'=>'value'); 失败返回false
  165. */
  166. function getTokenFromArray($arr) {
  167. if (isset($arr['access_token']) && $arr['access_token']) {
  168. $token = array();
  169. $this->access_token = $token['access_token'] = $arr['access_token'];
  170. if (isset($arr['refresh_token']) && $arr['refresh_token']) {
  171. $this->refresh_token = $token['refresh_token'] = $arr['refresh_token'];
  172. }
  173. return $token;
  174. } else {
  175. return false;
  176. }
  177. }
  178. /**
  179. * GET wrappwer for oAuthRequest.
  180. *
  181. * @return mixed
  182. */
  183. function get($url, $parameters = array()) {
  184. $response = $this->oAuthRequest($url, 'GET', $parameters);
  185. if ($this->format === 'json' && $this->decode_json) {
  186. return json_decode($response, true);
  187. }
  188. return $response;
  189. }
  190. /**
  191. * POST wreapper for oAuthRequest.
  192. *
  193. * @return mixed
  194. */
  195. function post($url, $parameters = array(), $multi = false) {
  196. $response = $this->oAuthRequest($url, 'POST', $parameters, $multi);
  197. if ($this->format === 'json' && $this->decode_json) {
  198. return json_decode($response, true);
  199. }
  200. return $response;
  201. }
  202. /**
  203. * DELTE wrapper for oAuthReqeust.
  204. *
  205. * @return mixed
  206. */
  207. function delete($url, $parameters = array()) {
  208. $response = $this->oAuthRequest($url, 'DELETE', $parameters);
  209. if ($this->format === 'json' && $this->decode_json) {
  210. return json_decode($response, true);
  211. }
  212. return $response;
  213. }
  214. /**
  215. * Format and sign an OAuth / API request
  216. *
  217. * @return string
  218. * @ignore
  219. */
  220. function oAuthRequest($url, $method, $parameters, $multi = false) {
  221. if (strrpos($url, 'http://') !== 0 && strrpos($url, 'https://') !== 0) {
  222. $url = "{$this->host}{$url}.{$this->format}";
  223. }
  224. switch ($method) {
  225. case 'GET':
  226. $url = $url . '?' . http_build_query($parameters);
  227. return $this->http($url, 'GET');
  228. default:
  229. $headers = array();
  230. if (!$multi && (is_array($parameters) || is_object($parameters))) {
  231. $body = http_build_query($parameters);
  232. } else {
  233. $body = self::build_http_query_multi($parameters);
  234. $headers[] = "Content-Type: multipart/form-data; boundary=" . self::$boundary;
  235. }
  236. return $this->http($url, $method, $body, $headers);
  237. }
  238. }
  239. /**
  240. * Make an HTTP request
  241. *
  242. * @return string API results
  243. * @ignore
  244. */
  245. function http($url, $method, $postfields = NULL, $headers = array()) {
  246. $this->http_info = array();
  247. $ci = curl_init();
  248. /* Curl settings */
  249. curl_setopt($ci, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
  250. curl_setopt($ci, CURLOPT_USERAGENT, $this->useragent);
  251. curl_setopt($ci, CURLOPT_CONNECTTIMEOUT, $this->connecttimeout);
  252. curl_setopt($ci, CURLOPT_TIMEOUT, $this->timeout);
  253. curl_setopt($ci, CURLOPT_RETURNTRANSFER, TRUE);
  254. curl_setopt($ci, CURLOPT_ENCODING, "");
  255. curl_setopt($ci, CURLOPT_SSL_VERIFYPEER, $this->ssl_verifypeer);
  256. // curl_setopt($ci, CURLOPT_SSL_VERIFYHOST, 1);
  257. curl_setopt($ci, CURLOPT_HEADERFUNCTION, array($this, 'getHeader'));
  258. curl_setopt($ci, CURLOPT_HEADER, FALSE);
  259. switch ($method) {
  260. case 'POST':
  261. curl_setopt($ci, CURLOPT_POST, TRUE);
  262. if (!empty($postfields)) {
  263. curl_setopt($ci, CURLOPT_POSTFIELDS, $postfields);
  264. $this->postdata = $postfields;
  265. }
  266. break;
  267. case 'DELETE':
  268. curl_setopt($ci, CURLOPT_CUSTOMREQUEST, 'DELETE');
  269. if (!empty($postfields)) {
  270. $url = "{$url}?{$postfields}";
  271. }
  272. }
  273. if (isset($this->access_token) && $this->access_token)
  274. $headers[] = "Authorization: OAuth2 " . $this->access_token;
  275. if (!empty($this->remote_ip)) {
  276. if (defined('SAE_ACCESSKEY')) {
  277. $headers[] = "SaeRemoteIP: " . $this->remote_ip;
  278. } else {
  279. $headers[] = "API-RemoteIP: " . $this->remote_ip;
  280. }
  281. } else {
  282. if (!defined('SAE_ACCESSKEY')) {
  283. $headers[] = "API-RemoteIP: " . $_SERVER['REMOTE_ADDR'];
  284. }
  285. }
  286. curl_setopt($ci, CURLOPT_URL, $url);
  287. curl_setopt($ci, CURLOPT_HTTPHEADER, $headers);
  288. curl_setopt($ci, CURLINFO_HEADER_OUT, TRUE);
  289. $response = curl_exec($ci);
  290. $this->http_code = curl_getinfo($ci, CURLINFO_HTTP_CODE);
  291. $this->http_info = array_merge($this->http_info, curl_getinfo($ci));
  292. $this->url = $url;
  293. if ($this->debug) {
  294. echo "=====post data======\r\n";
  295. var_dump($postfields);
  296. echo "=====headers======\r\n";
  297. print_r($headers);
  298. echo '=====request info=====' . "\r\n";
  299. print_r(curl_getinfo($ci));
  300. echo '=====response=====' . "\r\n";
  301. print_r($response);
  302. }
  303. curl_close($ci);
  304. return $response;
  305. }
  306. /**
  307. * 开启调试信息
  308. *
  309. * 开启调试信息后,SDK会将每次请求微博API所发送的POST Data、Headers以及请求信息、返回内容输出出来。
  310. *
  311. * @access public
  312. * @param bool $enable 是否开启调试信息
  313. * @return void
  314. */
  315. function set_debug($enable) {
  316. $this->debug = $enable;
  317. }
  318. /**
  319. * Get the header info to store.
  320. *
  321. * @return int
  322. * @ignore
  323. */
  324. function getHeader($ch, $header) {
  325. $i = strpos($header, ':');
  326. if (!empty($i)) {
  327. $key = str_replace('-', '_', strtolower(substr($header, 0, $i)));
  328. $value = trim(substr($header, $i + 2));
  329. $this->http_header[$key] = $value;
  330. }
  331. return strlen($header);
  332. }
  333. /**
  334. * @ignore
  335. */
  336. public static function build_http_query_multi($params) {
  337. if (!$params)
  338. return '';
  339. uksort($params, 'strcmp');
  340. $pairs = array();
  341. self::$boundary = $boundary = uniqid('------------------');
  342. $MPboundary = '--' . $boundary;
  343. $endMPboundary = $MPboundary . '--';
  344. $multipartbody = '';
  345. foreach ($params as $parameter => $value) {
  346. if (in_array($parameter, array('pic', 'image')) && $value{0} == '@') {
  347. $url = ltrim($value, '@');
  348. $content = file_get_contents($url);
  349. $array = explode('?', basename($url));
  350. $filename = $array[0];
  351. $multipartbody .= $MPboundary . "\r\n";
  352. $multipartbody .= 'Content-Disposition: form-data; name="' . $parameter . '"; filename="' . $filename . '"' . "\r\n";
  353. $multipartbody .= "Content-Type: image/unknown\r\n\r\n";
  354. $multipartbody .= $content . "\r\n";
  355. } else {
  356. $multipartbody .= $MPboundary . "\r\n";
  357. $multipartbody .= 'content-disposition: form-data; name="' . $parameter . "\"\r\n\r\n";
  358. $multipartbody .= $value . "\r\n";
  359. }
  360. }
  361. $multipartbody .= $endMPboundary;
  362. return $multipartbody;
  363. }
  364. }