OAuth 2.0 - 訪問(wèn)新浪、騰訊的資源服務(wù)器
I:訪問(wèn)資源服務(wù)器需要些什么?
訪問(wèn)資源服務(wù)器最最重要的前提條件就是你必須要有Access Token。而關(guān)于Access Token的取得原理已經(jīng)在前面兩篇(第1篇、第2篇)隨筆當(dāng)中有介紹,在這里就略過(guò)介紹Access Token的獲取方法了。
首先我們還是先看看新浪、騰訊他們的API文檔上提供的參考內(nèi)容。
新浪資源服務(wù)器API調(diào)用規(guī)范:
也就是說(shuō)新浪目前提供兩種方式去調(diào)用API.
第一種是以QueryString參數(shù)的形式去調(diào)用,例子:
https://api.weibo.com/2/{API接口}?access_token={yourAccessToken}
第二種是往header里添加Authorization:OAuth2 {yourAccessToken}的形式去調(diào)用。
騰訊資源服務(wù)器API調(diào)用規(guī)范:
根據(jù)騰訊方的文檔資料來(lái)看!騰訊目前提供的API調(diào)用方法跟新浪的第一種方式類似,但沒有提供類似新浪第二種把Access Token放到Header里面的方式(或許有但是我未能從文檔站上搜索到具體要求)。接著GRD的騰訊又搞了幾個(gè)屬于自己的參數(shù)如:openid, clientip, oauth_version,微微地創(chuàng)新了一把!關(guān)于OpenID騰訊的介紹是OpenID可以唯一標(biāo)識(shí)一個(gè)用戶。在同一個(gè)應(yīng)用下,同一個(gè)QQ號(hào)碼的OpenID是相同的;但在不同應(yīng)用下,同一個(gè)QQ號(hào)碼可能有不同的OpenID。另外樓主可以推斷這個(gè)oauth_version參數(shù)的設(shè)計(jì)能夠證明目前騰訊接口不是很穩(wěn)定,到時(shí)候估計(jì)會(huì)鬧2.b/2.c/2.d/2.e/2.*,或者過(guò)一定時(shí)間后oauth.net推出3.0版本時(shí),可以通過(guò)修改oauth_version去支持。
騰訊的例子:
https://open.t.qq.com/api/{API接口}?oauth_consumer_key={AppKey}&access_token={AccessToken}&openid={Openid}&clientip={ClientIP}&oauth_version=2.a&scope=all
在這說(shuō)一下,騰訊的參數(shù)中客戶端ip(clientip)可以不填(博主未確認(rèn)應(yīng)用部署上線后是否需要提供,因?yàn)閼?yīng)用都在開發(fā)期。。。)
ok,關(guān)于騰訊跟新浪的資源服務(wù)器API調(diào)用規(guī)范的重要部分已經(jīng)介紹完畢了。接下來(lái)放送新浪騰訊API列表,因?yàn)檎{(diào)用他們的API不單單只是提供Access Token還需要根據(jù)接口的說(shuō)明文檔區(qū)確認(rèn)是GET還是POST那些參數(shù)是可選的,那些參數(shù)是必選的之類。
II:訪問(wèn)資源服務(wù)器API示例
首先下載dotNetDR_OAuth2程序集 codeplex下載地址(介紹)
然后新建一個(gè)MVC3的應(yīng)用程序!將剛剛下載的OAuth2組件引用進(jìn)來(lái)。 (WebForm示例)
接著在項(xiàng)目代碼文件里添加你的AppKey, AppSecret 
然后再Home控制器的Index Action加上跳轉(zhuǎn)到新浪和騰訊微博授權(quán)頁(yè)面的超級(jí)鏈接。
HomeController.cs:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Dynamic;
//引入dotNetDR_OAuth2組件命名空間
using dotNetDR_OAuth2;
using dotNetDR_OAuth2.AccessToken;
namespace dotNetDR_OAuth2.Sample.MVC.Controllers
{
public class HomeController : Controller
{
//獲取新浪、騰訊的IAuthorizationCodeBase接口實(shí)例
private IAuthorizationCodeBase sina = AccessTokenFactory.Create(DefaultAppConfigs.Sina);
private IAuthorizationCodeBase tencent = AccessTokenFactory.Create(DefaultAppConfigs.Tencent);
public ActionResult Index()
{
dynamic model = new ExpandoObject();
//生成主機(jī)頭例如:http://www.yourhost.com:8081 (注:默認(rèn)80端口則不會(huì)顯示:80)
var hostPath = AccessTokenToolkit.GenerateHostPath(Request.Url);
//定義授權(quán)成功后返回的url地址
var sinaRedirectUrl = hostPath + Url.Action("Index", "Sina");
var tencentRedirectUrl = hostPath + Url.Action("Index", "Tencent");
//設(shè)置超級(jí)鏈接
model.SinaLink = sina.GenerateCodeUrl(sinaRedirectUrl);
model.TencentLink = tencent.GenerateCodeUrl(tencentRedirectUrl);
return View(model);
}
public ActionResult About()
{
return View();
}
}
}
然后Index.cshtml:
@{
ViewBag.Title = "Home Page";
}
@model dynamic
<h2>dotNetDR_OAuth2 微博API訪問(wèn)組件示例</h2>
<div>
@if (Model != null)
{
<a href="@Model.SinaLink"><img src="http://www.rzrgm.cn/Content/Images/xlwb.gif" />新浪微博登陸</a><text>|</text>
<a href="@Model.TencentLink"><img src="http://www.rzrgm.cn/Content/Images/txwb.gif" />騰訊微博登陸</a>
}
else
{
<h3>Error: Model沒有值</h3>
}
</div>
上圖是效果圖
接著我們建立各自的實(shí)現(xiàn):SinaController, TencentController.
新浪部分 - SinaController.cs:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
//導(dǎo)入組件命名空間
using dotNetDR_OAuth2;
using dotNetDR_OAuth2.AccessToken;
using dotNetDR_OAuth2.APIs.Providers.Sina;
namespace dotNetDR_OAuth2.Sample.MVC.Controllers
{
public class SinaController : Controller
{
private IAuthorizationCodeBase _authCode = AccessTokenFactory.Create(DefaultAppConfigs.Sina);
public ActionResult Index(string code)
{
if (Session["accessToken"] == null)
{
if (!string.IsNullOrEmpty(code))
{
var redirectUrl = AccessTokenToolkit.GenerateHostPath(Request.Url) + Url.Action("Index");
var accessToken = _authCode.GetResult(_authCode.GenerateAccessTokenUrl(redirectUrl, code));
if (Session["accessToken"] != null)
{
Session.Remove("accessToken");
}
Session.Add("accessToken", accessToken);
var hasAccessToken = new object();
return View(hasAccessToken);
}
else
{
return GotoIndex();
}
}
return View(new object());
}
public ActionResult ShowUserInfo()
{
if (Session["accessToken"] == null)
{
return GotoIndex();
}
var accessTokenObj = Session["accessToken"] as dynamic;
var uid = accessTokenObj.uid;
var accessToken = accessTokenObj.access_token;
var model = SinaApi.CallGet("users/show.json?uid=" + uid, accessToken);
SinaError err;
if (!SinaApi.HasError(model, out err))
{
return View(model);
}
else
{
Session["err"] = err;
return RedirectToAction("Error");
}
}
public ActionResult PublishMsg()
{
if (Session["accessToken"] == null)
{
return GotoIndex();
}
var accessTokenObj = Session["accessToken"] as dynamic;
var uid = accessTokenObj.uid;
var accessToken = accessTokenObj.access_token;
var msg = "Time: " + DateTime.Now.ToString("yyyy-MM-dd hh:mm:ss.fffff") + ": 這是一條來(lái)自dotNetDR_OAuth2組件發(fā)出的1條測(cè)試微博信息!";
var formData = new Dictionary<string, string>();
formData.Add("status", Server.UrlEncode(msg));
SinaError err;
var result = SinaApi.CallPost("statuses/update.json", accessToken, formData);
if (!SinaApi.HasError(result, out err))
{
return View();
}
else
{
Session["err"] = err;
return RedirectToAction("Error");
}
}
public ActionResult Error()
{
var err = Session["err"] as SinaError;
return View(err);
}
#region NonAction
[NonAction]
private ActionResult GotoIndex()
{
return RedirectToAction("Index", "Home");
}
#endregion
}
}
Index.cshtml:
@{
ViewBag.Title = "Index";
}
@model object
<h2>操作</h2>
<p>@Html.ActionLink("返回", "Index", "Home")</p>
@if (Model != null)
{
@Html.ActionLink("顯示用戶信息", "ShowUserInfo") <text> | </text>
@Html.ActionLink("發(fā)送測(cè)試微博", "PublishMsg")
}
PublishMsg.cshtml:
@{
ViewBag.Title = "PublishMsg";
}
<h2>發(fā)送成功</h2>
ShowUserInfo.cshtml:
@{
ViewBag.Title = "ShowUserInfo";
}
<h2>用戶:@Model.screen_name</h2>
<p>
用戶UID: @Model.id<br />
用戶昵稱: @Model.screen_name<br />
友好顯示名稱: @Model.name<br />
用戶所在地區(qū)ID: @Model.province<br />
用戶所在城市ID: @Model.city<br />
用戶所在地: @Model.location<br />
用戶描述: @Model.description<br />
用戶博客地址: @Model.url<br />
用戶頭像地址: @Model.profile_image_url @MvcHtmlString.Create(string.Format("<img src='{0}' />", Model.profile_image_url))<br />
用戶的個(gè)性化域名: @Model.domain<br />
性別(m:男、f:女、n:未知): @Model.gender<br />
粉絲數(shù): @Model.followers_count<br />
關(guān)注數(shù): @Model.friends_count<br />
微博數(shù): @Model.statuses_count<br />
收藏?cái)?shù): @Model.favourites_count<br />
創(chuàng)建時(shí)間: @Model.created_at<br />
當(dāng)前登錄用戶是否已關(guān)注該用戶: @Model.following<br />
是否允許所有人給我發(fā)私信: @Model.allow_all_act_msg<br />
是否允許帶有地理信息: @Model.geo_enabled<br />
是否是微博認(rèn)證用戶,即帶V用戶: @Model.verified<br />
是否允許所有人對(duì)我的微博進(jìn)行評(píng)論: @Model.allow_all_comment<br />
用戶大頭像地址: @Model.avatar_large @MvcHtmlString.Create(string.Format("<img src='{0}' />", @Model.avatar_large))<br />
認(rèn)證原因: @Model.verified_reason<br />
該用戶是否關(guān)注當(dāng)前登錄用戶: @Model.follow_me<br />
用戶的在線狀態(tài),0:不在線、1:在線: @Model.online_status<br />
用戶的互粉數(shù): @Model.bi_followers_count<br />
</p>
Error.cshtml:
@{
ViewBag.Title = "Error";
}
@model dotNetDR_OAuth2.APIs.Providers.Sina.SinaError
<h2>Error</h2>
<p>
error_code: @Model.error_code<br />
error: @Model.error<br />
request: @Model.request<br />
</p>
騰訊部分 - TencentController.cs:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using dotNetDR_OAuth2;
using dotNetDR_OAuth2.AccessToken;
using dotNetDR_OAuth2.APIs.Providers.Tencent;
namespace dotNetDR_OAuth2.Sample.MVC.Controllers
{
public class TencentController : Controller
{
private IAuthorizationCodeBase _authCode = AccessTokenFactory.Create(DefaultAppConfigs.Tencent);
//
// GET: /Tencent/
public ActionResult Index(string code, string openid, string openkey)
{
if (Session["accessToken"] == null)
{
if (!string.IsNullOrEmpty(code))
{
var redirectUrl = AccessTokenToolkit.GenerateHostPath(Request.Url) + Url.Action("Index");
var accessToken = _authCode.GetResult(_authCode.GenerateAccessTokenUrl(redirectUrl, code));
if (Session["accessToken"] != null)
{
Session.Remove("accessToken");
}
accessToken.openid = openid; //注意GRD騰訊自家的微創(chuàng)新
accessToken.openkey = openkey; //注意GRD騰訊自家的微創(chuàng)新
Session.Add("accessToken", accessToken);
var hasAccessToken = new object();
return View(hasAccessToken);
}
else
{
return GotoIndex();
}
}
return View(new object());
}
public ActionResult ShowUserInfo()
{
if (Session["accessToken"] == null)
{
return GotoIndex();
}
var accessTokenObj = Session["accessToken"] as dynamic;
var uid = accessTokenObj.name;
var accessToken = accessTokenObj.access_token;
var openid = accessTokenObj.openid;
var model = TencentApi.CallGet("user/info?format=json", accessToken, openid);
TencentError err;
if (!TencentApi.HasError(model, out err))
{
var realModel = model.data;
return View(realModel);
}
else
{
Session["err"] = err;
return RedirectToAction("Error");
}
}
public ActionResult PublishMsg()
{
if (Session["accessToken"] == null)
{
return GotoIndex();
}
var accessTokenObj = Session["accessToken"] as dynamic;
var uid = accessTokenObj.name;
var accessToken = accessTokenObj.access_token;
var openid = accessTokenObj.openid;
var msg = "Time: " + DateTime.Now.ToString("yyyy-MM-dd hh:mm:ss.fffff") + ": 這是一條來(lái)自dotNetDR_OAuth2組件發(fā)出的1條測(cè)試微博信息!";
var formData = new Dictionary<string, string>();
formData.Add("content", Server.UrlEncode(msg));
TencentError err;
var result = TencentApi.CallPost("t/add?format=json", accessToken, openid, formData);
if (!TencentApi.HasError(result, out err))
{
return View();
}
else
{
Session["err"] = err;
return RedirectToAction("Error");
}
}
public ActionResult Error()
{
var err = Session["err"] as TencentError;
return View(err);
}
#region NonAction
[NonAction]
private ActionResult GotoIndex()
{
return RedirectToAction("Index", "Home");
}
#endregion
}
}
Index.cshtml:
@{
ViewBag.Title = "Index";
}
@model object
<h2>操作</h2>
<p>@Html.ActionLink("返回", "Index", "Home")</p>
@if (Model != null)
{
@Html.ActionLink("顯示用戶信息", "ShowUserInfo") <text> | </text>
@Html.ActionLink("發(fā)送測(cè)試微博", "PublishMsg")
}
PublishMsg.cshtml:
@{
ViewBag.Title = "PublishMsg";
}
<h2>發(fā)送成功</h2>
ShowUserInfo.cshtml:
@{
ViewBag.Title = "ShowUserInfo";
}
<h2>用戶昵稱: @Model.nick</h2>
<p>
出生天: @Model.birth_day <br />
出生月: @Model.birth_month <br />
出生年: @Model.birth_year <br />
城市id: @Model.city_code <br />
國(guó)家id: @Model.country_code <br />
郵箱: @Model.email <br />
聽眾數(shù): @Model.fansnum <br />
收藏?cái)?shù): @Model.favnum <br />
頭像url: @Model.head @MvcHtmlString.Create(string.Format("<img src=\"{0}/50\" />", @Model.head)) <br />
家鄉(xiāng)所在城市id: @Model.homecity_code <br />
家鄉(xiāng)所在國(guó)家id: @Model.homecountry_code <br />
個(gè)人主頁(yè): @Model.homepage <br />
家鄉(xiāng)所在省id: @Model.homeprovince_code <br />
家鄉(xiāng)所在城鎮(zhèn)id: @Model.hometown_code <br />
收聽的人數(shù): @Model.idolnum <br />
行業(yè)id: @Model.industry_code <br />
個(gè)人介紹: @Model.introduction <br />
是否企業(yè)機(jī)構(gòu): @Model.isent <br />
是否在當(dāng)前用戶的黑名單中,0-不是,1-是: @Model.ismyblack <br />
是否是當(dāng)前用戶的聽眾,0-不是,1-是: @Model.ismyfans <br />
是否是當(dāng)前用戶的偶像,0-不是,1-是: @Model.ismyidol <br />
是否實(shí)名認(rèn)證,0-老用戶,1-已實(shí)名認(rèn)證,2-未實(shí)名認(rèn)證: @Model.isrealname <br />
是否認(rèn)證用戶: @Model.isvip <br />
所在地: @Model.location <br />
互聽好友數(shù): @Model.mutual_fans_num <br />
用戶帳戶名: @Model.name <br />
用戶唯一id,與name相對(duì)應(yīng): @Model.openid <br />
地區(qū)id: @Model.province_code <br />
注冊(cè)時(shí)間: @Model.regtime <br />
是否允許所有人給當(dāng)前用戶發(fā)私信,0-僅有偶像,1-名人+聽眾,2-所有人: @Model.send_private_flag <br />
用戶性別,1-男,2-女,0-未填寫: @Model.sex <br />
發(fā)表的微博數(shù): @Model.tweetnum <br />
認(rèn)證信息: @Model.verifyinfo <br />
</p>
Error.cshtml:
@{
ViewBag.Title = "Error";
}
@model dotNetDR_OAuth2.APIs.Providers.Tencent.TencentError
<h2>Error</h2>
<p>
ret: @Model.ret<br />
errcode: @Model.errcode<br />
msg: @Model.msg<br />
----------------------<br />
errcode=1 無(wú)效TOKEN,被吊銷<br />
errcode=2 請(qǐng)求重放<br />
errcode=3 access_token不存在<br />
errcode=4 access_token超時(shí)<br />
errcode=5 oauth 版本不對(duì)<br />
errcode=6 oauth 簽名方法不對(duì)<br />
errcode=7 參數(shù)錯(cuò)<br />
errcode=8 處理失敗<br />
errcode=9 驗(yàn)證簽名失敗<br />
errcode=10 網(wǎng)絡(luò)錯(cuò)誤<br />
errcode=11 參數(shù)長(zhǎng)度不對(duì)<br />
errcode=12 處理失敗<br />
errcode=13 處理失敗<br />
errcode=14 處理失敗<br />
errcode=15 處理失敗<br />
</p>
在這里重復(fù)上一下效果圖吧!


發(fā)送微博的效果我就不貼了!!
這里附上一個(gè)各位OAuth開發(fā)者或許需要的流程圖(專家請(qǐng)盡情噴小菜)

III:dotNetDR_OAuth2 組件介紹
在上一節(jié)里面的代碼!大家都可以看到這個(gè)組件已經(jīng)隱藏了System.Net.HttpWebRequest, System.Net.HttpWebResponse這些細(xì)節(jié),而且返回的值都是dynamic類型的,這樣一下。我們就僅需要對(duì)這新浪或者騰訊的API文檔來(lái)逐步調(diào)試了,因?yàn)椴┲鞑豢赡茉诮M件里把每一個(gè)接口返回值得都定義成一個(gè)C#類文件:


如果都定義成claas我太累了,所以用.NET 4.0 提供的dynamic算了,更加具體內(nèi)容我打算另外用一遍隨筆去介紹!!轉(zhuǎn)載的請(qǐng)聲明及保留好出處!!
組件作者:博客園dotNetDR_ http://www.rzrgm.cn/highend/
IV:Access Token的過(guò)期時(shí)間
新浪:

騰訊:

V:示例項(xiàng)目代碼
注意:當(dāng)你在測(cè)試環(huán)境下時(shí),必須要把windows系統(tǒng)的hosts文件添加好具體的域名地址指向你本機(jī),例如博主的:

然后就是需要打開dotNetDR_OAuth2.Sample.MVC.csproj手動(dòng)更改IIS路徑

廣告:OAuth2.0 組件討論群:108441512 詢問(wèn)C#以外的OAuth 2.0 sdk同學(xué)勿入!謝謝合作。
本文到此結(jié)束!如果覺得文章對(duì)你幫助很大的,請(qǐng)點(diǎn)擊[推薦]~謝謝。




浙公網(wǎng)安備 33010602011771號(hào)