[转载]OAuth 2.0 – 访问新浪、腾讯的资源服务器 – dotNetDR_ – 博客园.
I:访问资源服务器需要些什么?
访问资源服务器最最重要的前提条件就是你必须要有Access Token。而关于Access Token的取得原理已经在前面两篇(第1篇、第2篇)随笔当中有介绍,在这里就略过介绍Access Token的获取方法了。
首先我们还是先看看新浪、腾讯他们的API文档上提供的参考内容。
新浪资源服务器API调用规范:
也就是说新浪目前提供两种方式去调用API.
第一种是以QueryString参数的形式去调用,例子:
https://api.weibo.com/2/{API接口}?access_token={yourAccessToken}
第二种是往header里添加Authorization:OAuth2 {yourAccessToken}的形式去调用。
腾讯资源服务器API调用规范:
根 据腾讯方的文档资料来看!腾讯目前提供的API调用方法跟新浪的第一种方式类似,但没有提供类似新浪第二种把Access Token放到Header里面的方式(或许有但是我未能从文档站上搜索到具体要求)。接着GRD的腾讯又搞了几个属于自己的参数如:openid, clientip, oauth_version,微微地创新了一把!关于OpenID腾讯的介绍是OpenID可以唯一标识一个用户。在同一个应用下,同一个QQ号码的OpenID是相同的;但在不同应用下,同一个QQ号码可能有不同的OpenID。另外楼主可以推断这个oauth_version参数的设计能够证明目前腾讯接口不是很稳定,到时候估计会闹2.b/2.c/2.d/2.e/2.*,或者过一定时间后oauth.net推出3.0版本时,可以通过修改oauth_version去支持。
腾讯的例子:
https://open.t.qq.com /api/{API接口}?oauth_consumer_key={AppKey}&access_token={AccessToken}& amp;openid={Openid}&clientip={ClientIP}&oauth_version=2.a&scope=all
在这说一下,腾讯的参数中客户端ip(clientip)可以不填(博主未确认应用部署上线后是否需要提供,因为应用都在开发期。。。)
ok,关于腾讯跟新浪的资源服务器API调用规范的重要部分已经介绍完毕了。接下来放送新浪腾讯API列表,因为调用他们的API不单单只是提供Access Token还需要根据接口的说明文档区确认是GET还是POST那些参数是可选的,那些参数是必选的之类。
II:访问资源服务器API示例
首先下载dotNetDR_OAuth2程序集 codeplex下载地址(介绍)
然后新建一个MVC3的应用程序!将刚刚下载的OAuth2组件引用进来。 (WebForm示例)
接着在项目代码文件里添加你的AppKey, AppSecret
然后再Home控制器的Index Action加上跳转到新浪和腾讯微博授权页面的超级链接。
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接口实例 private IAuthorizationCodeBase sina = AccessTokenFactory.Create(DefaultAppConfigs.Sina); private IAuthorizationCodeBase tencent = AccessTokenFactory.Create(DefaultAppConfigs.Tencent); public ActionResult Index() { dynamic model = new ExpandoObject(); //生成主机头例如:http://www.yourhost.com:8081 (注:默认80端口则不会显示:80) var hostPath = AccessTokenToolkit.GenerateHostPath(Request.Url); //定义授权成功后返回的url地址 var sinaRedirectUrl = hostPath + Url.Action("Index", "Sina"); var tencentRedirectUrl = hostPath + Url.Action("Index", "Tencent"); //设置超级链接 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访问组件示例</h2> <div>@if (Model != null) { <a href="@Model.SinaLink"><img src="http://www.cnblogs.com/Content/Images/xlwb.gif" alt="" />新浪微博登陆</a>| <a href="@Model.TencentLink"><img src="http://www.cnblogs.com/Content/Images/txwb.gif" alt="" />腾讯微博登陆</a> } else { <h3>Error: Model没有值</h3> } </div>
上图是效果图
接着我们建立各自的实现:SinaController, TencentController.
新浪部分 – SinaController.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.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") + ": 这是一条来自dotNetDR_OAuth2组件发出的1条测试微博信息!"; var formData = new Dictionary(); 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> @Html.ActionLink("返回", "Index", "Home") @if (Model != null) { @Html.ActionLink("显示用户信息", "ShowUserInfo") | @Html.ActionLink("发送测试微博", "PublishMsg") }
PublishMsg.cshtml:
@{ ViewBag.Title = "PublishMsg"; } <h2>发送成功</h2>
ShowUserInfo.cshtml:
@{ ViewBag.Title = "ShowUserInfo"; } <h2>用户:@Model.screen_name</h2> 用户UID: @Model.id 用户昵称: @Model.screen_name 友好显示名称: @Model.name 用户所在地区ID: @Model.province 用户所在城市ID: @Model.city 用户所在地: @Model.location 用户描述: @Model.description 用户博客地址: @Model.url 用户头像地址: @Model.profile_image_url @MvcHtmlString.Create(string.Format("<img src="{0}" alt="" />", Model.profile_image_url)) 用户的个性化域名: @Model.domain 性别(m:男、f:女、n:未知): @Model.gender 粉丝数: @Model.followers_count 关注数: @Model.friends_count 微博数: @Model.statuses_count 收藏数: @Model.favourites_count 创建时间: @Model.created_at 当前登录用户是否已关注该用户: @Model.following 是否允许所有人给我发私信: @Model.allow_all_act_msg 是否允许带有地理信息: @Model.geo_enabled 是否是微博认证用户,即带V用户: @Model.verified 是否允许所有人对我的微博进行评论: @Model.allow_all_comment 用户大头像地址: @Model.avatar_large @MvcHtmlString.Create(string.Format("<img src="{0}" alt="" />", @Model.avatar_large)) 认证原因: @Model.verified_reason 该用户是否关注当前登录用户: @Model.follow_me 用户的在线状态,0:不在线、1:在线: @Model.online_status 用户的互粉数: @Model.bi_followers_count
Error.cshtml:
@{ ViewBag.Title = "Error"; } @model dotNetDR_OAuth2.APIs.Providers.Sina.SinaError <h2>Error</h2> error_code: @Model.error_code error: @Model.error request: @Model.request
腾讯部分 – 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腾讯自家的微创新 accessToken.openkey = openkey; //注意GRD腾讯自家的微创新 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") + ": 这是一条来自dotNetDR_OAuth2组件发出的1条测试微博信息!"; var formData = new Dictionary(); 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> @Html.ActionLink("返回", "Index", "Home") @if (Model != null) { @Html.ActionLink("显示用户信息", "ShowUserInfo") | @Html.ActionLink("发送测试微博", "PublishMsg") }
PublishMsg.cshtml:
@{ ViewBag.Title = "PublishMsg"; } <h2>发送成功</h2>
ShowUserInfo.cshtml:
@{ ViewBag.Title = "ShowUserInfo"; } <h2>用户昵称: @Model.nick</h2> 出生天: @Model.birth_day 出生月: @Model.birth_month 出生年: @Model.birth_year 城市id: @Model.city_code 国家id: @Model.country_code 邮箱: @Model.email 听众数: @Model.fansnum 收藏数: @Model.favnum 头像url: @Model.head @MvcHtmlString.Create(string.Format("<img src="\"{0}/50\"" alt="" />", @Model.head)) 家乡所在城市id: @Model.homecity_code 家乡所在国家id: @Model.homecountry_code 个人主页: @Model.homepage 家乡所在省id: @Model.homeprovince_code 家乡所在城镇id: @Model.hometown_code 收听的人数: @Model.idolnum 行业id: @Model.industry_code 个人介绍: @Model.introduction 是否企业机构: @Model.isent 是否在当前用户的黑名单中,0-不是,1-是: @Model.ismyblack 是否是当前用户的听众,0-不是,1-是: @Model.ismyfans 是否是当前用户的偶像,0-不是,1-是: @Model.ismyidol 是否实名认证,0-老用户,1-已实名认证,2-未实名认证: @Model.isrealname 是否认证用户: @Model.isvip 所在地: @Model.location 互听好友数: @Model.mutual_fans_num 用户帐户名: @Model.name 用户唯一id,与name相对应: @Model.openid 地区id: @Model.province_code 注册时间: @Model.regtime 是否允许所有人给当前用户发私信,0-仅有偶像,1-名人+听众,2-所有人: @Model.send_private_flag 用户性别,1-男,2-女,0-未填写: @Model.sex 发表的微博数: @Model.tweetnum 认证信息: @Model.verifyinfo
Error.cshtml:
@{ ViewBag.Title = "Error"; } @model dotNetDR_OAuth2.APIs.Providers.Tencent.TencentError <h2>Error</h2> ret: @Model.ret errcode: @Model.errcode msg: @Model.msg ---------------------- errcode=1 无效TOKEN,被吊销 errcode=2 请求重放 errcode=3 access_token不存在 errcode=4 access_token超时 errcode=5 oauth 版本不对 errcode=6 oauth 签名方法不对 errcode=7 参数错 errcode=8 处理失败 errcode=9 验证签名失败 errcode=10 网络错误 errcode=11 参数长度不对 errcode=12 处理失败 errcode=13 处理失败 errcode=14 处理失败 errcode=15 处理失败
在这里重复上一下效果图吧!
发送微博的效果我就不贴了!!
这里附上一个各位OAuth开发者或许需要的流程图(专家请尽情喷小菜)
III:dotNetDR_OAuth2 组件介绍
在上一节里面的代码!大家都可以看到这个组件已经隐藏了System.Net.HttpWebRequest, System.Net.HttpWebResponse这些细节,而且返回的值都是dynamic类型的,这样一下。我们就仅需要对这新浪或者腾讯的 API文档来逐步调试了,因为博主不可能在组件里把每一个接口返回值得都定义成一个C#类文件:
如果都定义成claas我太累了,所以用.NET 4.0 提供的dynamic算了,更加具体内容我打算另外用一遍随笔去介绍!!转载的请声明及保留好出处!!
组件作者:博客园dotNetDR_ http://www.cnblogs.com/highend/
IV:Access Token的过期时间
新浪:
腾讯:
V:示例项目代码
注意:当你在测试环境下时,必须要把windows系统的hosts文件添加好具体的域名地址指向你本机,例如博主的:
然后就是需要打开dotNetDR_OAuth2.Sample.MVC.csproj手动更改IIS路径
广告:ASP.NET MVC 3.0 QQ交流群:33353329 (可用于讨论当前的OAuth 2.0) 询问C#以外的OAuth 2.0 sdk同学勿入!谢谢合作。