zoukankan      html  css  js  c++  java
  • 新浪微博API Oauth2.0 认证

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

    本意是在注销账号前保留之前的一些数据。决定用python 爬取收藏。可是未登录无法爬取。想要登录有两种办法,伪造浏览器登录。第二就是注册新浪开发者账号,通过Oauth认证调用其API。

    Oauth 的原理搞了一天才明白。很多网站都提供多语言的Oauth。而 1.0 和 2.0 的最大差别就是多了一个 callback 回调页面。关于这方面的说明很少,搞得我一头雾水折腾了好久。总算明白了。

    Oauth的原理

    现在多用 oauth2.0. 具体可以看其官方文档。用下面的图简单说明:


    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 当参数传给服务器。

    这样说比较抽象,举个简单的例子:

    有一个人 U 想去银行 S 取钱。U 没事情去柜台,就委托朋友 C 去柜台。当C 去了银行S,给S说:我要帮 U 取钱。S 先验证 C 的身份,是合法公民。然后 S 打电话给 U。说:C 要帮你取钱,你确定么,确定的话就输入用户名和密码。 U 确定了。此时,S 就给了一个 钥匙给 C。说:我们不提供自动服务,给你钥匙,自己去库房取。然后 C 拿着钥匙,就去取钱了。取完之后给力 U。U 很感激。

    通过上面的解释,应该可以知道整个过程中有三个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======
    ";
    198             var_dump($postfields);
    199                             
    200             echo '=====info====='."
    ";
    201             print_r( curl_getinfo($ci) );
    202                             
    203             echo '=====$response====='."
    ";
    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 . "
    ";
    250                 $multipartbody .= 'Content-Disposition: form-data; name="' . $parameter . '"; filename="' . $filename . '"'. "
    ";
    251                 $multipartbody .= "Content-Type: image/unknown
    
    ";
    252                 $multipartbody .= $content. "
    ";
    253             } else {
    254                 $multipartbody .= $MPboundary . "
    ";
    255                 $multipartbody .= 'content-disposition: form-data; name="' . $parameter . ""
    
    ";
    256                 $multipartbody .= $value."
    ";
    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认证,额外的收获吧。总而言之,人总是在逼迫中才能进步。

  • 相关阅读:
    MATLAB中的SFunction的用法(C语言)
    OpenGL中的转换矩阵
    MATLAB读取和写入Excel文件
    一阶微分方程的求解
    三对角矩阵(Tridiagonal Matrices)的求法:Thomas Algorithm(TDMA)
    CarSim与Simulink联合仿真
    三次样条插值(Cubic Spline Interpolation)及代码实现(C语言)
    在Simulink中创建库
    轮胎的魔术公式(Magic Fomula)模型
    C#中的编译时的类型与运行时的类型
  • 原文地址:https://www.cnblogs.com/zquancai/p/4024400.html
Copyright © 2011-2022 走看看