新浪微博API Oauth2.0 認證

原文連接: http://rsj217.diandian.com/post/2013-04-17/40050093587 php

本意是在註銷帳號前保留以前的一些數據。決定用python 爬取收藏。但是未登陸沒法爬取。想要登陸有兩種辦法,僞造瀏覽器登陸。第二就是註冊新浪開發者帳號,經過Oauth認證調用其API。html

Oauth 的原理搞了一天才明白。不少網站都提供多語言的Oauth。而 1.0 和 2.0 的最大差異就是多了一個 callback 回調頁面。關於這方面的說明不多,搞得我一頭霧水折騰了很久。總算明白了。html5

Oauth的原理python

如今多用 oauth2.0. 具體能夠看其官方文檔。用下面的圖簡單說明:
web


Consumer指應用程序,User是用戶,Service是服務器。大體過程爲,User使用程序Consumer。C 向 Service請求一個未受權的令牌(Request_token),這個時候 S 驗證 C的合法性(經過開發者的 APP_KEY 和 APP_SECREST)。而後重定向到一個受權頁面(一般由服務方提供)。此時用戶進行受權,即輸入用戶名和密碼,用戶會自動向服務器發送 authorize request_token。獲得受權的 request_token。再而後,受權經過後,跳轉到一個 callback 頁面。經過request_token用來向服務器請求,換取 acess_token。至此,認證結束。接下來要請求數據,只須要根據文檔 把 acess_token 當參數傳給服務器。json

這樣說比較抽象,舉個簡單的例子:canvas

有一我的 U 想去銀行 S 取錢。U 沒事情去櫃檯,就委託朋友 C 去櫃檯。當C 去了銀行S,給S說:我要幫 U 取錢。S 先驗證 C 的身份,是合法公民。而後 S 打電話給 U。說:C 要幫你取錢,你肯定麼,肯定的話就輸入用戶名和密碼。 U 肯定了。此時,S 就給了一個 鑰匙給 C。說:咱們不提供自動服務,給你鑰匙,本身去庫房取。而後 C 拿着鑰匙,就去取錢了。取完以後給力 U。U 很感激。api

經過上面的解釋,應該能夠知道整個過程當中有三個URL請求地址,以下圖:瀏覽器


這寫地址由服務方提供,上面那個就是 qq 公共平臺的url。新浪有新浪本身的。關於第一步,請求 request_token的時候,服務器也要認證開發者賬號,也就是銀行認證 C 的合法身份。安全

每一個 開發者都有app_key 和 app_secret.  app_key稱爲密鑰, app_secret爲密匙。開發者和服務都知道。而後 ,開發者經過  app_key 和 app_secret 經過 一種加密(sha1)獲得一個字符串secretcode 公鑰。再把 app_key 和 secretcode 發送給服務器。服務器接收以後,將發送到 app_key和服務器存儲的 app_secret 經過一樣的加密手段獲得一個 secretcode2 和發送過來的secretcode進行對比,若是同樣,則能夠證實是經過。這一點好處是安全。不怕被抓包。

新浪 Oauth的運用

簡單知道原理,就能夠進行認證。

準備工做

須要向新浪申請開發者賬號,而後建立一個應用。以後會獲得一個 app_key 和 app_secret。須要注意是必定要填寫下面的回調地址:


新浪的是能夠設置 本地的,例如 http://127.0.0.1/oauth/callback.php

豆瓣的須要公網能夠訪問的回調地址,不一樣api不同。

佈署

能夠下載 官方提供的 SDK ,根據語言選擇。這裏選擇 php。理由是 php的web環境能夠一鍵安裝。須要注意到是,新浪的 SDK須要 php 開啓 curl。否則會報錯。

文檔結構以下:

config.php  開發者配置文件

index.php  登陸主入口

callback.php 回調處理

saetv2.ex.class.php  SDK 主文件,提供認證和api調用

weibolist.php 認證成功以後的數據請求和展現

引用官方的 SDK ,根據demo使用就行。這裏重寫一下 SDK 的請求過程。

index.php

 1 <?php  2 session_start();  3                                                     
 4 include_once( 'config.php' );  5 include_once( 'saetv2.ex.class.php' );  6                                                     
 7 $o = new SaeTOAuthV2( WB_AKEY , WB_SKEY );  8 //獲取請求 request_token 的 url
 9 $code_url = $o->getAuthorizeURL( WB_CALLBACK_URL ); 10                                                     
11 ?>
12 <!DOCTYPE html>
13 <html>
14 <head>
15 <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
16 <title>新浪微博登陸</title>
17 </head>
18                                                     
19 <body>
20     <!-- 受權按鈕 -->
21     <p><a href="<?php echo $code_url?>"><img src="weibo_login.png" title="點擊進入受權頁面" alt="點擊進入受權頁面" border="0" /></a></p>
22 </body>
23 </html>
View Code

 

主要作的事情和I獲取 請求    request_token 的url。而後跳轉到官方提供的受權頁面:


 

受權以後會重定向到回調 callback.pho.

 1 <?php  2 session_start();  3                                    
 4 include_once( 'config.php' );  5 include_once( 'saetv2.ex.class.php' );  6                                    
 7 $o = new SaeTOAuthV2( WB_AKEY , WB_SKEY );  8                                    
 9 if (isset($_REQUEST['code'])) { 10     $keys = array(); 11     $keys['code'] = $_REQUEST['code']; 12     $keys['redirect_uri'] = WB_CALLBACK_URL; 13     try { 14         $token = $o->getAccessToken( 'code', $keys ) ; 15     } catch (OAuthException $e) { 16  } 17 } 18                                    
19 if ($token) { 20     $_SESSION['token'] = $token; 21     setcookie( 'weibojs_'.$o->client_id, http_build_query($token) ); 22 ?>
23 受權完成,<a href="weibolist.php">進入你的微博列表頁面</a><br />
24 <?php 25 } else { 26 ?>
27 受權失敗。 28 <?php 29 } 30 ?>
View Code

 

這一步主要是根據受權以後返回的 code 進行調用,請求 acess_token。

前面兩部的方法 ,都在

saetv2.ex.class.php裏實現,代碼以下:

 1 <?php  2 /**  3  * @ignore  4  */
 5 class OAuthException extends Exception {  6     // pass
 7 }  8                             
 9                             
 10 /**  11  * 新浪微博 OAuth 認證類(OAuth2)  12  *  13  * 受權機制說明請你們參考微博開放平臺文檔:{@link http://open.weibo.com/wiki/Oauth2}  14  *  15  * @package sae  16  * @author Elmer Zhang  17  * @version 1.0  18  */
 19 class SaeTOAuthV2 {  20                             
 21     public $client_id;  22     public $client_secret;  23     public $access_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 = 'Sae T OAuth2 v0.1';  34     public $debug = FALSE;  35     public static $boundary = '';  36                             
 37     /**  38  * Set API URLS  39      */
 40     /**  41  * @ignore  42      */
 43     function accessTokenURL()  { return 'https://api.weibo.com/oauth2/access_token'; }  44     /**  45  * @ignore  46      */
 47     function authorizeURL()    { return 'https://api.weibo.com/oauth2/authorize'; }  48                             
 49     /**  50  * construct WeiboOAuth object  51      */
 52     function __construct($client_id, $client_secret, $access_token = NULL) {  53         $this->client_id = $client_id;  54         $this->client_secret = $client_secret;  55         $this->access_token = $access_token;  56  }  57                             
 58     /**  59  * authorize接口  60  *  61  * 對應API:{@link http://open.weibo.com/wiki/Oauth2/authorize Oauth2/authorize}  62  *  63  * @param string $url 受權後的回調地址,站外應用需與回調地址一致,站內應用須要填寫canvas page的地址  64  * @param string $response_type 支持的值包括 code 和token 默認值爲code  65  * @param string $state 用於保持請求和回調的狀態。在回調時,會在Query Parameter中回傳該參數  66  * @param string $display 受權頁面類型 可選範圍:  67  * - default 默認受權頁面  68  * - mobile 支持html5的手機  69  * - popup 彈窗受權頁  70  * - wap1.2 wap1.2頁面  71  * - wap2.0 wap2.0頁面  72  * - js js-sdk 專用 受權頁面是彈窗,返回結果爲js-sdk回掉函數  73  * - apponweibo 站內應用專用,站內應用不傳display參數,而且response_type爲token時,默認使用改display.受權後不會返回access_token,只是輸出js刷新站內應用父框架  74  * @return array  75      */
 76     function getAuthorizeURL( $url, $response_type = 'code', $state = NULL, $display = NULL ) {  77         $params = array();  78         $params['client_id'] = $this->client_id;  79         $params['redirect_uri'] = $url;  80         $params['response_type'] = $response_type;  81         $params['state'] = $state;  82         $params['display'] = $display;  83         return $this->authorizeURL() . "?" . http_build_query($params);  84  }  85                             
 86     /**  87  * access_token接口  88  *  89  * 對應API:{@link http://open.weibo.com/wiki/OAuth2/access_token OAuth2/access_token}  90  *  91  * @param string $type 請求的類型,能夠爲:code, password, token  92  * @param array $keys 其餘參數:  93  * - 當$type爲code時: array('code'=>..., 'redirect_uri'=>...)  94  * - 當$type爲password時: array('username'=>..., 'password'=>...)  95  * - 當$type爲token時: array('refresh_token'=>...)  96  * @return array  97      */
 98     function getAccessToken( $type = 'code', $keys ) {  99         $params = array(); 100         $params['client_id'] = $this->client_id; 101         $params['client_secret'] = $this->client_secret; 102         if ( $type === 'code' ) { 103             $params['grant_type'] = 'authorization_code'; 104             $params['code'] = $keys['code']; 105             $params['redirect_uri'] = $keys['redirect_uri']; 106         } else { 107             throw new OAuthException("wrong auth type"); 108  } 109                             
110         $response = $this->oAuthRequest($this->accessTokenURL(), 'POST', $params); 111         $token = json_decode($response, true); 112         if ( is_array($token) && !isset($token['error']) ) { 113             $this->access_token = $token['access_token']; 114         } else { 115             throw new OAuthException("get access token failed." . $token['error']); 116  } 117         return $token; 118  } 119                             
120     /** 121  * Format and sign an OAuth / API request 122  * 123  * @return string 124  * @ignore 125      */
126     function oAuthRequest($url, $method, $parameters, $multi = false) { 127                             
128         if (strrpos($url, 'http://') !== 0 && strrpos($url, 'https://') !== 0) { 129             $url = "{$this->host}{$url}.{$this->format}"; 130  } 131                             
132         switch ($method) { 133             case 'GET':
134                 $url = $url . '?' . http_build_query($parameters); 135                 return $this->http($url, 'GET'); 136             default:
137                 $headers = array(); 138                 if (!$multi && (is_array($parameters) || is_object($parameters)) ) { 139                     $body = http_build_query($parameters); 140                 } else { 141                     $body = self::build_http_query_multi($parameters); 142                     $headers[] = "Content-Type: multipart/form-data; boundary=" . self::$boundary; 143  } 144                 return $this->http($url, $method, $body, $headers); 145  } 146  } 147                             
148     /** 149  * Make an HTTP request 150  * 151  * @return string API results 152  * @ignore 153      */
154     function http($url, $method, $postfields = NULL, $headers = array()) { 155         $this->http_info = array(); 156         $ci = curl_init(); 157         /* Curl settings */
158         curl_setopt($ci, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0); 159         curl_setopt($ci, CURLOPT_USERAGENT, $this->useragent); 160         curl_setopt($ci, CURLOPT_CONNECTTIMEOUT, $this->connecttimeout); 161         curl_setopt($ci, CURLOPT_TIMEOUT, $this->timeout); 162         curl_setopt($ci, CURLOPT_RETURNTRANSFER, TRUE); 163         curl_setopt($ci, CURLOPT_ENCODING, ""); 164         curl_setopt($ci, CURLOPT_SSL_VERIFYPEER, $this->ssl_verifypeer); 165         curl_setopt($ci, CURLOPT_HEADERFUNCTION, array($this, 'getHeader')); 166         curl_setopt($ci, CURLOPT_HEADER, FALSE); 167                             
168         switch ($method) { 169             case 'POST':
170                 curl_setopt($ci, CURLOPT_POST, TRUE); 171                 if (!empty($postfields)) { 172                     curl_setopt($ci, CURLOPT_POSTFIELDS, $postfields); 173                     $this->postdata = $postfields; 174  } 175                 break; 176             case 'DELETE':
177                 curl_setopt($ci, CURLOPT_CUSTOMREQUEST, 'DELETE'); 178                 if (!empty($postfields)) { 179                     $url = "{$url}?{$postfields}"; 180  } 181  } 182                             
183         if ( isset($this->access_token) && $this->access_token ) 184             $headers[] = "Authorization: OAuth2 ".$this->access_token; 185                             
186         $headers[] = "API-RemoteIP: " . $_SERVER['REMOTE_ADDR']; 187         curl_setopt($ci, CURLOPT_URL, $url ); 188         curl_setopt($ci, CURLOPT_HTTPHEADER, $headers ); 189         curl_setopt($ci, CURLINFO_HEADER_OUT, TRUE ); 190                             
191         $response = curl_exec($ci); 192         $this->http_code = curl_getinfo($ci, CURLINFO_HTTP_CODE); 193         $this->http_info = array_merge($this->http_info, curl_getinfo($ci)); 194         $this->url = $url; 195                             
196         if ($this->debug) { 197             echo "=====post data======\r\n"; 198             var_dump($postfields); 199                             
200             echo '=====info====='."\r\n"; 201             print_r( curl_getinfo($ci) ); 202                             
203             echo '=====$response====='."\r\n"; 204             print_r( $response ); 205  } 206         curl_close ($ci); 207         return $response; 208  } 209                             
210     /** 211  * Get the header info to store. 212  * 213  * @return int 214  * @ignore 215      */
216     function getHeader($ch, $header) { 217         $i = strpos($header, ':'); 218         if (!empty($i)) { 219             $key = str_replace('-', '_', strtolower(substr($header, 0, $i))); 220             $value = trim(substr($header, $i + 2)); 221             $this->http_header[$key] = $value; 222  } 223         return strlen($header); 224  } 225                             
226     /** 227  * @ignore 228      */
229     public static function build_http_query_multi($params) { 230         if (!$params) return ''; 231                             
232         uksort($params, 'strcmp'); 233                             
234         $pairs = array(); 235                             
236         self::$boundary = $boundary = uniqid('------------------'); 237         $MPboundary = '--'.$boundary; 238         $endMPboundary = $MPboundary. '--'; 239         $multipartbody = ''; 240                             
241         foreach ($params as $parameter => $value) { 242                             
243             if( in_array($parameter, array('pic', 'image')) && $value{0} == '@' ) { 244                 $url = ltrim( $value, '@' ); 245                 $content = file_get_contents( $url ); 246                 $array = explode( '?', basename( $url ) ); 247                 $filename = $array[0]; 248                             
249                 $multipartbody .= $MPboundary . "\r\n"; 250                 $multipartbody .= 'Content-Disposition: form-data; name="' . $parameter . '"; filename="' . $filename . '"'. "\r\n"; 251                 $multipartbody .= "Content-Type: image/unknown\r\n\r\n"; 252                 $multipartbody .= $content. "\r\n"; 253             } else { 254                 $multipartbody .= $MPboundary . "\r\n"; 255                 $multipartbody .= 'content-disposition: form-data; name="' . $parameter . "\"\r\n\r\n"; 256                 $multipartbody .= $value."\r\n"; 257  } 258                             
259  } 260                             
261         $multipartbody .= $endMPboundary; 262         return $multipartbody; 263  } 264 } 265 ?>
View Code

getAuthorizeURL 方法是用來獲取請求 request_token的地址。

getAccessToken 方法是獲取 access_token

oAuthRequest 方法是用來發送請求

getHeader 我沒發現有地方調用,可是沒有他有不行,暫時不知道爲何。

至此Oauth認證結束。

認證就是爲了得倒 access_token。原本我是要爬數據。才爲了登陸。後來發現直接使用 新浪的 api 測試接口,自動生成access_token。爬蟲就直接用。固然,早沒有發現,才促使我去研究了 Oauth認證,額外的收穫吧。總而言之,人老是在逼迫中才能進步。

相關文章
相關標籤/搜索