基于PECL OAuth打造微博应用
最近,国内主要门户网站相继开放了微博平台,对开发者而言这无疑是个利好消息,不过在实际使用中却发现平台质量良莠不齐,有很多不完善的地方,就拿PHP版SDK来说吧,多半都是用TwitterOAuth改的,一旦多平台集成,很容易出现命名冲突之类的问题。
既然官方SDK不给力,那我们只能发扬自力更生的革命精神了!好消息是PHP本身已经有了一个标准的OAuth实现:PECL OAuth!下面以此为例来讲解一下如何实现微博应用:
说明:首先需要对OAuth概念有一定的了解,如不清楚可以参考我以前写的文章:OAuth那些事儿,其次需要注册成为各个微博平台(新浪,腾讯,搜狐,网易)的开发者,拿到属于你自己的CONSUMER_KEY和CONSUMER_SECRET(有时也被称作APP_*)。
下面开始!假定我们要开发一个类似Follow5和微博通的应用,简单点说就是把消息同时发送到多个微博平台,出于安全性的考虑,不会使用HTTP Basic,而会使用OAuth,这就需要我们先拿到Access Token和Access Token Secret。
以新浪微博为例,大致的代码如下:
以下是代码片段:
<?php
session_start();
$request_token_url = ’http://api.t.sina.com.cn/oauth/request_token’;
$authorize_url = ’http://api.t.sina.com.cn/oauth/authorize’;
$access_token_url = ’http://api.t.sina.com.cn/oauth/access_token’;
$oauth = new OAuth(
’YOUR_CONSUMER_KEY’,
’YOUR_CONSUMER_SECRET’,
OAUTH_SIG_METHOD_HMACSHA1,
OAUTH_AUTH_TYPE_FORM
);
if (empty($_GET[’oauth_verifier’])) {
$callback_url = "http://{$_SERVER[’HTTP_HOST’]}{$_SERVER[’REQUEST_URI’]}";
$request_token = $oauth->getRequestToken($request_token_url);
$_SESSION[’oauth_token_secret’] = $request_token[’oauth_token_secret’];
$param = array(
’oauth_token’ => $request_token[’oauth_token’],
’oauth_callback’ => $callback_url
);
header("Location: {$authorize_url}?" . http_build_query($param));
exit;
}
$oauth->setToken($_GET[’oauth_token’], $_SESSION[’oauth_token_secret’]);
$access_token = $oauth->getAccessToken(
$access_token_url, null, $_GET[’oauth_verifier’]
);
var_dump($access_token);
?>
腾讯微博相比较而言有点特殊,大致代码如下:
以下是代码片段:
<?php
session_start();
$request_token_url = ’https://open.t.qq.com/cgi-bin/request_token’;
$authorize_url = ’https://open.t.qq.com/cgi-bin/authorize’;
$access_token_url = ’https://open.t.qq.com/cgi-bin/access_token’;
$oauth = new OAuth(
’YOUR_CONSUMER_KEY’,
’YOUR_CONSUMER_SECRET’,
OAUTH_SIG_METHOD_HMACSHA1,
OAUTH_AUTH_TYPE_FORM
);
$oauth->setNonce(md5(mt_rand()));
if (empty($_GET[’oauth_verifier’])) {
$callback_url = "http://{$_SERVER[’HTTP_HOST’]}{$_SERVER[’REQUEST_URI’]}";
$request_token = $oauth->getRequestToken($request_token_url, $callback_url);
$_SESSION[’oauth_token_secret’] = $request_token[’oauth_token_secret’];
$param = array(
’oauth_token’ => $request_token[’oauth_token’]
);
header("Location: {$authorize_url}?" . http_build_query($param));
exit;
}
$oauth->setToken($_GET[’oauth_token’], $_SESSION[’oauth_token_secret’]);
$access_token = $oauth->getAccessToken(
$access_token_url, null, $_GET[’oauth_verifier’]
);
var_dump($access_token);
?>
注意:参数nonce和callback的设置,详见:使用 PECL 的 OAuth 库访问 QQ 微博 API。
照猫画虎就能得到搜狐和网易的Access Token和Access Token Secret了,我就不罗嗦了。
下面继续做我们的微博应用,发消息一般都是文本形式的,不过有中国特色的微薄开放平台支持文本加图片的方式:图片上传到服务器,但本身并不参与签名。这和标准OAuth是冲突的,所以要扩展一下PECL OAuth,并且尽可能兼容原类的使用方法和习惯:
以下是代码片段:
<?php
class MicroblogOAuth extends OAuth
{
public $consumer_key;
public $signature_method;
public $auth_type;
public $nonce;
public $timestamp;
public $token;
public $version;
public $request_engine;
public $last_response;
public function setAuthType($auth_type)
{
if (parent::setAuthType($auth_type)) {
$this->auth_type = $auth_type;
return true;
}
return false;
}
public function setNonce($nonce)
{
if (parent::setNonce($nonce)) {
$this->nonce = $nonce;
return true;
}
return false;
}
public function setTimestamp($timestamp)
{
if (parent::setTimestamp($timestamp)) {
$this->timestamp = $timestamp;
return true;
}
return false;
}
public function setToken($token, $token_secret)
{
if (parent::setToken($token, $token_secret)) {
$this->token = $token;
return true;
}
return false;
}
public function setVersion($version)
{
if (parent::setVersion($version)) {
$this->version = $version;
return true;
}
return false;
}
public function setRequestEngine($request_engine)
{
try {
parent::setRequestEngine($request_engine);
$this->request_engine = $request_engine;
} catch(OAuthException $e) {
echo $e->getMessage();
}
}
public function getLastResponse()
{
return parent::getLastResponse() ?: $this->last_response;
}
public function upload($url, $file, $param = array(), $header = array())
{
$boundary = sprintf(’%010d’, mt_rand());
$header[] = "Content-Type: multipart/form-data; boundary={$boundary}";
$oauth = array(
’oauth_consumer_key’ => $this->consumer_key,
’oauth_nonce’ => $this->nonce,
’oauth_signature_method’ => $this->signature_method,
’oauth_timestamp’ => $this->timestamp,
’oauth_token’ => $this->token,
’oauth_version’ => $this->version,
);
if ($this->auth_type == OAUTH_AUTH_TYPE_FORM) {
$param += $oauth;
$param[’oauth_signature’] = $this->generateSignature(
OAUTH_HTTP_METHOD_POST, $url, $param
);
} else {
$oauth_header = array();
$oauth[’oauth_signature’] = $this->generateSignature(
OAUTH_HTTP_METHOD_POST, $url, $param
);
foreach ($oauth AS $name => $value) {
$oauth_header[] = $name . ’="’ . $value . ’"’;
}
$header[] = ’Authorization: OAuth ’ . implode(’, ’, $oauth_header);
}
$content_disposition = function($name, $filename = null) {
$result = ’Content-Disposition: form-data; name="’ . $name . ’"’;
if ($filename !== null) {
$result .= ’; filename="’ . $filename . ’"’;
}
return $result;
};
$content = array();
foreach ($file as $name => $value) {
$filename = pathinfo($value, PATHINFO_BASENAME);
switch(strtolower(pathinfo($filename, PATHINFO_EXTENSION))) {
case ’gif’;
$mime = ’image/gif’;
break;
case ’jpeg’:
case ’jpg’:
$mime = ’image/jpg’;
break;
case ’png’;
$mime = ’image/png’;
break;
default:
$mime = ’application/octet-stream’;
}
$content_type = "Content-Type: {$mime}";
$content[] = "--{$boundary}";
$content[] = $content_disposition($name, $filename);
$content[] = $content_type;
$content[] = ’’;
$content[] = file_get_contents($value);
}
ksort($param);
foreach ($param as $name => $value) {
$content[] = "--{$boundary}";
$content[] = $content_disposition($name);
$content[] = ’’;
$content[] = $value;
}
$content[] = "--{$boundary}--";
$content[] = ’’;
$content = implode("\r\n", $content);
if ($this->request_engine == OAUTH_REQENGINE_CURL) {
$curl = curl_init();
curl_setopt($curl, CURLOPT_POST, true);
curl_setopt($curl, CURLOPT_POSTFIELDS, $content);
curl_setopt($curl, CURLOPT_HTTPHEADER, $header);
curl_setopt($curl, CURLOPT_URL, $url);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($curl);
curl_close($curl);
} else {
$header[] = ’Connection: close’;
$context = stream_context_create(array(
’http’ => array(
’protocol_version’ => ’1.1’,
’method’ => ’POST’,
’content’ => $content,
’header’ => implode("\r\n", $header),
)
));
$response = file_get_contents($url, false, $context);
}
if ($response) {
$this->last_response = $response;
return true;
}
return false;
}
}
?>
说明:如果使用PHP Streams方式发送请求的话,需要注意HTTP版本的问题,否则某些情况下请求会被防火墙拦截,详见:由于 HTTP request 不规范导致的被防火墙拦截。
新类MicroblogOAuth直接扩展自PECL的OAuth类!随着PHP内核API的逐渐类化,这样的扩展方式将会越来越常见,值得开发人员重视。
为了让调用方式更统一,使用工厂方法包装MicroblogOAuth的实例化过程:
以下是代码片段:
<?php
function OAuth($consumer_key, $consumer_secret, $signature_method, $auth_type)
{
$instance = new MicroblogOAuth(
$consumer_key,
$consumer_secret,
$signature_method,
$auth_type
);
$instance->consumer_key = $consumer_key;
$instance->signature_method = $signature_method;
$instance->setAuthType($auth_type);
$instance->setNonce(md5(mt_rand()));
$instance->setTimestamp(time());
$instance->setVersion(’1.0’);
if (extension_loaded(’curl’)) {
$instance->setRequestEngine(OAUTH_REQENGINE_CURL);
} else {
$instance->setRequestEngine(OAUTH_REQENGINE_STREAMS);
}
$instance->last_response = null;
return $instance;
}
?>
先看看搜狐是如何发送文本加图片消息的:
以下是代码片段:
<?php
$text = ’hello, world.’;
$image = ’http://www.foo.com/bar.gif’;
$oauth = OAuth(
’YOUR_CONSUMER_KEY’,
’YOUR_CONSUMER_SECRET’,
OAUTH_SIG_METHOD_HMACSHA1,
OAUTH_AUTH_TYPE_AUTHORIZATION
);
$oauth->setToken(
’YOUR_ACCESS_TOKEN’,
’YOUR_ACCESS_TOKEN_SECRET’
);
$oauth->upload(
’http://api.t.sohu.com/statuses/upload.json’,
array(’pic’ => $image),
array(’status’ => oauth_urlencode($text))
);
$result = json_decode($oauth->getLastResponse(), true);
var_dump($result);
?>
说明:搜狐要求文本要先编码,然后和图片一起发送,这点不同于其它微博开放平台。
再看看网易是如何发送文本加图片消息的:
以下是代码片段:
<?php
$text = ’hello, world.’;
$image = ’http://www.foo.com/bar.gif’;
$oauth = OAuth(
’YOUR_CONSUMER_KEY’,
’YOUR_CONSUMER_SECRET’,
OAUTH_SIG_METHOD_HMACSHA1,
OAUTH_AUTH_TYPE_AUTHORIZATION
);
$oauth->setToken(
’YOUR_ACCESS_TOKEN’,
’YOUR_ACCESS_TOKEN_SECRET’
);
$oauth->upload(
’http://api.t.163.com/statuses/upload.json’,
array(’pic’ => $image)
);
$result = json_decode($oauth->getLastResponse(), true);
if (isset($result[’upload_image_url’])) {
$text .= " {$result[’upload_image_url’]}";
}
$oauth->fetch(
’http://api.t.163.com/statuses/update.json’,
array(’status’ => $text),
OAUTH_HTTP_METHOD_POST
);
$result = json_decode($oauth->getLastResponse(), true);
var_dump($result);
?>
说明:网易发送文本加图片消息是分两步实现的,先上传图片,然后把图片的URL附加在文本信息的后面再发送到服务器,这点不同于其它微博开放平台。
收工!微博开放平台的使用并没有太多复杂的地方,仔细看文档调试,一般的问题都很容易解决。有了上面的基础代码,只要再使用适配器模式分别包装一下各个微博平台,很容易就能实现一套通用SDK,搞定新浪,腾讯,搜狐,网易!
建议继续学习:
- Twitter/微博客的学习摘要 (阅读:8134)
- 新浪微博OAuth认证流程分析 (阅读:4078)
- 微博进入肉搏时代 (阅读:4048)
- 给微博打上标签 (阅读:3913)
- 深入理解OAuth与豆瓣OAuth test (阅读:3877)
- 微博架构与平台安全演讲稿 (阅读:3787)
- 在sae中利用SaeFetchurl进行豆瓣的OAuth授权 (阅读:3586)
- PHP for Twitter OAuth 教学演示 (阅读:3540)
- 新浪微博开放平台初探 (阅读:3508)
- 构建可扩展的微博架构(qcon beijing 2010演讲) (阅读:3196)
扫一扫订阅我的微信号:IT技术博客大学习
- 作者:老王 来源: 火丁笔记
- 标签: OAuth PECL 应用 微博
- 发布时间:2011-01-16 22:36:09
- [55] Oracle MTS模式下 进程地址与会话信
- [55] IOS安全–浅谈关于IOS加固的几种方法
- [54] 图书馆的世界纪录
- [54] 如何拿下简短的域名
- [53] android 开发入门
- [52] Go Reflect 性能
- [49] 读书笔记-壹百度:百度十年千倍的29条法则
- [49] 【社会化设计】自我(self)部分――欢迎区
- [38] 程序员技术练级攻略
- [33] 视觉调整-设计师 vs. 逻辑