OAuth1.0实践之foursquare客户端同步到饭否

来源:互联网 发布:服务器端口有多少 编辑:程序博客网 时间:2024/05/16 13:46

上回说完了《OAuth2.0实践之foursquare客户端登录》这回还是要回来说OAuth1.0。

还是我写的那个foursquare的web客户端程序。其中加上了同步到饭否的功能,原来用的是BasicAuth,不过饭否宣布从今年元旦开始关闭BasicAuth,全面改用OAuth1.0。于是我又不得不赶在截止前几天把这部分程序改写了——还好不像上次那样完全重写,改起来还是很快的。不过其中因为OAuth1.0的签名部分影响到了几个基础函数,改了一些地方,弄出了些坏味道。因为反正用php就是为了quick and dirty,也就懒得去重构什么的了,能用就行。

同样,这里也不讨论程序的其它部分,只谈一下同步到饭否的部分。

按照标准的OAuth1.0登录流程应该是这样的:

1、在服务提供方注册一个客户端应用,取得一对consumer_key和consumer_secret;

2、通过调用提供方的oauth request_token取得一个临时token;

3、凭这个临时token和回调地址转向到提供方的authorize页面,用户在这个页面上决定是否授权;

4、用户授权后,提供方转向到回调地址,并提供一个verifier码;

5、凭这个verifier码和临时token调用提供方的oauth access_token取得正式的access_token;

6、使用access_token调用提供方的API。

以上全部调用请求中都必须附上consumer_key/secret,并且加以数字签名。

很明显,比OAuth2.0要复杂很多,还好这个技术已经比较成熟,有很多现成的库可以用。因为我的虚拟主机目前还是只支持PHP4,所以用的是一个可以支持PHP4的库:lib_oauth.php。那个流行的OAuth.php库其实稍微改改也可以,我原来也试过,不过还是觉得用现成的方便些,就换成这个lib_oauth了。

顺便吐槽一句,国内这些网站号称提供API的,个个实现都有妖蛾子,并不像国外网站做得那么规范。尤其以新浪和腾迅为甚,网易和搜狐略好,不过搜狐也有问题,OAuth1.0不支持Head方式,而且对参数也有一些特别的要求。至于饭否,它最妖的地方就是:居然没有 verifier 码!估计是通过时间和IP之类的来鉴定的吧。

好吧,具体到这个应用上来。我需要实现的功能包括两块:一是登录饭否,二是通过饭否API发消息。

首先是按饭否官方方式申请一对key/secret。

然后用类似foursquare登录部分的方式实现一个饭否登录:

用户点一个链接,程序调用request_token并重定向到饭否的authorize,用户确认授权以后回调回来,程序再调用access_token取得正式的access_token(注意:这里没有verifier)并回以保存。

最后,在调用饭否API前用OAuth1.0规范进行请求签名。

具体的登录代码如下:

function fanfou_login() {    session_start();    $GLOBALS['user']['fantype'] = 'oauth';    if ($oauth_token = $_GET['oauth_token']) {  //  来自饭否的回调        $response = fanfou_process(FANFOU_OAUTH.'access_token');  //  直接申请access_token,与标准不同的是没有verifier        parse_str($response, $token);        $GLOBALS['user']['fanpass'] = $token['oauth_token'] .'|'.$token['oauth_token_secret'];        unset($_SESSION['oauth_request_token_secret']);        $user = fanfou_process(FANFOU_API.'account/verify_credentials.json');  //  调用一个API功能以验证登录是否成功        $GLOBALS['user']['fanuser'] = $user->id;        _user_save_cookie(1);        header('Location: '.BASE_URL.SF_CALLBACK.'?'.http_build_query(array('redir' => BASE_URL)));        exit();    } else {  //  用户选择登录饭否        $response = fanfou_process(FANFOU_OAUTH.'request_token');  //  取得临时token        parse_str($response, $token);        $_SESSION['oauth_request_token_secret'] = $token['oauth_token_secret'];        $authorise_url = FANFOU_OAUTH.'authorize?oauth_token='.$token['oauth_token']."&oauth_callback=".BASE_URL.FANFOU_CALLBACK;        header("Location: $authorise_url");  //  转向饭否的authorize页面,请求用户授权    }}

取得access_token以后以及取得之前的请求,都需要按规范加入consumer_key/consumer_secret,并加以签名(签名内容包括规范要求的所有字段,不过这部分工作已经由oauth库处理了)。

function fan_oauth_sign(&$url, &$args) {    require_once 'lib_oauth.php';  //  引用 lib_oauth 库    $method = $args !== false ? 'POST' : 'GET';    if (preg_match_all('#[?&]([^=]+)=([^&]+)#', $url, $matches, PREG_SET_ORDER)) {        foreach ($matches as $match) {            $args[$match[1]] = $match[2];        }        $url = substr($url, 0, strpos($url, '?'));    }    if (($oauth_token = $_GET['oauth_token']) && $_SESSION['oauth_request_token_secret']) {        $oauth_token_secret = $_SESSION['oauth_request_token_secret'];    } else {        list($oauth_token, $oauth_token_secret) = explode('|', $GLOBALS['user']['fanpass']);    }        $keys = array(            'oauth_key'        => OAUTH_CONSUMER_KEY,            'oauth_secret'    => OAUTH_CONSUMER_SECRET,            'user_key'      => $oauth_token,            'user_secret'   => $oauth_token_secret,        );    $url = oauth_sign_get($keys, $url, $args, $method);  // OAuth1.0签名    if ($method == 'POST'){        list($url, $args) = explode('?', $url, 2);    }else{        $args = false;    }}function fanfou_process($url, $post_data = false) {    fan_oauth_sign($url, $post_data);    $result = api_process($url, $post_data);    $response = $result['response'];    $response_info = $result['response_info'];    //  错误处理及json解码之类,从略……}

这部分代码看起来比OAuth2.0并没有复杂太多,但这是在使用了oauth库的情况下才有这样的简化。用OAuth2.0的话则不需要依赖别的库就可以实现,而且还要简单。

整个客户端代码下载在Google Code。


原创粉丝点击