[转载]OAuth 2.0 - 访问新浪、腾讯的资源服务器

[转载]OAuth 2.0 – 访问新浪、腾讯的资源服务器 – dotNetDR_ – 博客园.

I:访问资源服务器需要些什么?

访问资源服务器最最重要的前提条件就是你必须要有Access Token。而关于Access Token的取得原理已经在前面两篇(第1篇第2篇)随笔当中有介绍,在这里就略过介绍Access Token的获取方法了。

首先我们还是先看看新浪、腾讯他们的API文档上提供的参考内容。

新浪资源服务器API调用规范
image
也就是说新浪目前提供两种方式去调用API.
第一种是以QueryString参数的形式去调用,例子:
https://api.weibo.com/2/{API接口}?access_token={yourAccessToken}

第二种是往header里添加Authorization:OAuth2 {yourAccessToken}的形式去调用。

腾讯资源服务器API调用规范
image
根 据腾讯方的文档资料来看!腾讯目前提供的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那些参数是可选的,那些参数是必选的之类。

新浪API接口地址腾讯API接口地址

附:腾讯接口Q&A新浪接口Q&A

II:访问资源服务器API示例

首先下载dotNetDR_OAuth2程序集 codeplex下载地址介绍

然后新建一个MVC3的应用程序!将刚刚下载的OAuth2组件引用进来。 (WebForm示例
image

接着在项目代码文件里添加你的AppKey, AppSecret
image

然后再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>

image

上图是效果图

接着我们建立各自的实现: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="\&quot;{0}/50\&quot;" 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 处理失败

在这里重复上一下效果图吧!

image

image

发送微博的效果我就不贴了!!

这里附上一个各位OAuth开发者或许需要的流程图(专家请尽情喷小菜)

image

visio文件下载

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的过期时间

新浪

image

腾讯

image

V:示例项目代码

注意:当你在测试环境下时,必须要把windows系统的hosts文件添加好具体的域名地址指向你本机,例如博主的:

image

然后就是需要打开dotNetDR_OAuth2.Sample.MVC.csproj手动更改IIS路径

image

代码下载

广告:ASP.NET MVC 3.0 QQ交流群:33353329 (可用于讨论当前的OAuth 2.0) 询问C#以外的OAuth 2.0 sdk同学勿入!谢谢合作。

赞(0) 打赏
分享到: 更多 (0)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

微信扫一扫打赏