使用DotNetOpenAuth来实现有道云笔记的授权

     最近由于公司的需求特别多,而且还不是一个项目的需求,几个数据库几个VS来回切换。难免搞混搞乱,忘记。于是自己用WPF做了一个小工具:AgileToDo,一个待办列表。本来使用sqlce来做本地的存储。但是在这个云时代,搞本地存储显然不能拿出手。于是我决定给它集成有道云笔记,使用有道云笔记的的OPEN API来实现云端的存储。http://note.youdao.com/open/apidoc.html

     有道云笔记的API使用OAuth的方式对第三方应用进行授权。OAuth的介绍就不多说了,说的人也挺多了,自己查资料吧。要使用它的API,我们需要授权。本来想完全有自己去实现一下这个过程的,不过看了授权的整个过程发现还是很复杂的,不花点时间估计还真搞不出来。于是用nuget搜一下发现了DotNetOpenAuth,而且这个类库的排名是很靠前的,想必是个神器。

这里简单说一下DotNetOpenAuth,据我简单了解,DotNetOpenAuth主要是分了2部分的功能。一部分类库是作为消费者,访问第三方OAUTH服务,请求授权,比如我现在要做的事,访问有道云笔记的OAUTH服务。还有一部分类库是用做服务方,实现OAUTH服务提供方的功能,可以使你的用户系统支持OAUTH,供第三方消费者来使用。要深入了解的请直接去下载源码,而且里面包含了很多sample,有web的有desktop的,访问google,twitter的例子等等等。我的代码当然也是参考了这些sample。

      经过一晚上的折腾,终于用.NET实现了有道云笔记的授权。废话不多说,上代码吧。

1.添加DotNetOpenAuth的引用

image

2.YDAuthBaseInfo类

代码语言:javascript
复制
    /// <summary>
    /// OAUTH授权所需的一些基础信息
    /// </summary>
    public class YDAuthBaseInfo
    {
        public static readonly string OwnerId = "";
        public static readonly string ConsumerName = "";
        public static readonly string ConsumerKey = "";//开发者申请的KEY
        public static readonly string ConsumerSecret = "";//开发者申请的Secret
    public static readonly string BaseUrl = &#34;http://sandbox.note.youdao.com&#34;;//测试沙箱基础url
    public static readonly ServiceProviderDescription ServiceDescription = null;//OAUTH服务提供方信息
    static YDAuthBaseInfo()
    {
        OwnerId = &#34;kklldog&#34;;
        ConsumerName = &#34;AgileToDo&#34;;
        ConsumerKey = &#34;xxxx&#34;;
        ConsumerSecret = &#34;xxxx&#34;;
        ServiceDescription = new ServiceProviderDescription
        {
            RequestTokenEndpoint = new MessageReceivingEndpoint(YDAuthBaseInfo.BaseUrl + &#34;/oauth/request_token&#34;,
                HttpDeliveryMethods.AuthorizationHeaderRequest | HttpDeliveryMethods.GetRequest),
            UserAuthorizationEndpoint = new MessageReceivingEndpoint(YDAuthBaseInfo.BaseUrl + &#34;/oauth/authorize&#34;,
                HttpDeliveryMethods.AuthorizationHeaderRequest | HttpDeliveryMethods.GetRequest),
            AccessTokenEndpoint = new MessageReceivingEndpoint(YDAuthBaseInfo.BaseUrl + &#34;/oauth/access_token&#34;, 
                HttpDeliveryMethods.AuthorizationHeaderRequest | HttpDeliveryMethods.GetRequest),
            TamperProtectionElements = new ITamperProtectionChannelBindingElement[] { new HmacSha1SigningBindingElement() },
        };
    }


}</code></pre></div></div><p>3.YDTokenManager 类</p><div class="rno-markdown-code"><div class="rno-markdown-code-toolbar"><div class="rno-markdown-code-toolbar-info"><div class="rno-markdown-code-toolbar-item is-type"><span class="is-m-hidden">代码语言:</span>javascript</div></div><div class="rno-markdown-code-toolbar-opt"><div class="rno-markdown-code-toolbar-copy"><i class="icon-copy"></i><span class="is-m-hidden">复制</span></div></div></div><div class="developer-code-block"><pre class="prism-token token line-numbers language-javascript"><code class="language-javascript" style="margin-left:0">    /// &lt;summary&gt;
/// TokenManager 令牌管理
/// &lt;/summary&gt;
public class YDTokenManager : IConsumerTokenManager
{
    private Dictionary&lt;string, string&gt; _tokensAndSecrets = new Dictionary&lt;string, string&gt;();
    private TokenType _tokenType;
    /// &lt;summary&gt;
    /// Initializes a new instance of the &lt;see cref=&#34;YDTokenManager&#34;/&gt; class.
    /// &lt;/summary&gt;
    /// &lt;param name=&#34;consumerKey&#34;&gt;The consumer key.&lt;/param&gt;
    /// &lt;param name=&#34;consumerSecret&#34;&gt;The consumer secret.&lt;/param&gt;
    public YDTokenManager(string consumerKey, string consumerSecret)
    {
        if (string.IsNullOrEmpty(consumerKey))
        {
            throw new ArgumentNullException(&#34;consumerKey&#34;);
        }

        this.ConsumerKey = consumerKey;
        this.ConsumerSecret = consumerSecret;
    }

    /// &lt;summary&gt;
    /// Gets the consumer key.
    /// &lt;/summary&gt;
    /// &lt;value&gt;The consumer key.&lt;/value&gt;
    public string ConsumerKey { get; private set; }

    /// &lt;summary&gt;
    /// Gets the consumer secret.
    /// &lt;/summary&gt;
    /// &lt;value&gt;The consumer secret.&lt;/value&gt;
    public string ConsumerSecret { get; private set; }

    #region ITokenManager Members


    public string GetTokenSecret(string token)
    {
        return this._tokensAndSecrets[token];
    }


    public void StoreNewRequestToken(UnauthorizedTokenRequest request, ITokenSecretContainingMessage response)
    {
        this._tokensAndSecrets[response.Token] = response.TokenSecret;
        _tokenType = TokenType.RequestToken;
    }

    public void ExpireRequestTokenAndStoreNewAccessToken(string consumerKey, string requestToken, string accessToken, string accessTokenSecret)
    {
        this._tokensAndSecrets.Remove(requestToken);
        this._tokensAndSecrets[accessToken] = accessTokenSecret;
        _tokenType = TokenType.AccessToken;
    }

    /// &lt;summary&gt;
    /// Classifies a token as a request token or an access token.
    /// &lt;/summary&gt;
    /// &lt;param name=&#34;token&#34;&gt;The token to classify.&lt;/param&gt;
    /// &lt;returns&gt;Request or Access token, or invalid if the token is not recognized.&lt;/returns&gt;
    public TokenType GetTokenType(string token)
    {
        return _tokenType;
    }

    #endregion
}</code></pre></div></div><p>4.YDWebConsumer类</p><div class="rno-markdown-code"><div class="rno-markdown-code-toolbar"><div class="rno-markdown-code-toolbar-info"><div class="rno-markdown-code-toolbar-item is-type"><span class="is-m-hidden">代码语言:</span>javascript</div></div><div class="rno-markdown-code-toolbar-opt"><div class="rno-markdown-code-toolbar-copy"><i class="icon-copy"></i><span class="is-m-hidden">复制</span></div></div></div><div class="developer-code-block"><pre class="prism-token token line-numbers language-javascript"><code class="language-javascript" style="margin-left:0">    /// &lt;summary&gt;
/// 有道OPEN AUTH的web端消费者实现
/// &lt;/summary&gt;
public class YDWebConsumer:WebConsumer
{
    public YDWebConsumer(ServiceProviderDescription serviceProvider, IConsumerTokenManager tokenManager)
        : base(serviceProvider, tokenManager)
    {
    }
    /// &lt;summary&gt;
    /// 请求授权
    /// &lt;/summary&gt;
    /// &lt;param name=&#34;consumer&#34;&gt;&lt;/param&gt;
    public static void RequestAuthorization(YDWebConsumer consumer)
    {
        if (consumer == null)
        {
            throw new ArgumentNullException(&#34;YDWebConsumer&#34;);
        }

        Uri callback = GetCallbackUrlFromContext();
        var request = consumer.PrepareRequestUserAuthorization(callback, null, null);
        consumer.Channel.Send(request);
    }

    /// &lt;summary&gt;
    /// 获取CALLBACKURL
    /// &lt;/summary&gt;
    /// &lt;returns&gt;&lt;/returns&gt;
    internal static Uri GetCallbackUrlFromContext()
    {
        Uri callback = MessagingUtilities.GetRequestUrlFromContext().StripQueryArgumentsWithPrefix(&#34;oauth_&#34;);
        return callback;
    }
}</code></pre></div></div><p>5.有了这些我们就可以去请求授权了。让我们来试试,新建一个ASP.NET项目,在Default.aspx下修改代码</p><div class="rno-markdown-code"><div class="rno-markdown-code-toolbar"><div class="rno-markdown-code-toolbar-info"><div class="rno-markdown-code-toolbar-item is-type"><span class="is-m-hidden">代码语言:</span>javascript</div></div><div class="rno-markdown-code-toolbar-opt"><div class="rno-markdown-code-toolbar-copy"><i class="icon-copy"></i><span class="is-m-hidden">复制</span></div></div></div><div class="developer-code-block"><pre class="prism-token token line-numbers language-javascript"><code class="language-javascript" style="margin-left:0">&lt;%@ Page Title=&#34;主页&#34; Language=&#34;C#&#34; MasterPageFile=&#34;~/Site.master&#34; AutoEventWireup=&#34;true&#34;
CodeBehind=&#34;Default.aspx.cs&#34; Inherits=&#34;YDOpenAPI._Default&#34; %&gt;

<asp:Content ID="HeaderContent" runat="server" ContentPlaceHolderID="HeadContent">
</asp:Content>
<asp:Content ID="BodyContent" runat="server" ContentPlaceHolderID="MainContent">
<h2>
欢迎使用 ASP.NET!
</h2>
<asp:Label runat="server" ID="lbl"></asp:Label>
</asp:Content>

代码语言:javascript
复制
        protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
var youDao = new YDWebConsumer(YDAuthBaseInfo.ServiceDescription, this.TokenManager);

            // 是否已经授权</code></pre></div></div><div class="rno-markdown-code"><div class="rno-markdown-code-toolbar"><div class="rno-markdown-code-toolbar-info"><div class="rno-markdown-code-toolbar-item is-type"><span class="is-m-hidden">代码语言:</span>javascript</div></div><div class="rno-markdown-code-toolbar-opt"><div class="rno-markdown-code-toolbar-copy"><i class="icon-copy"></i><span class="is-m-hidden">复制</span></div></div></div><div class="developer-code-block"><pre class="prism-token token line-numbers language-javascript"><code class="language-javascript" style="margin-left:0">                var accessTokenResponse = youDao.ProcessUserAuthorization();
            if (accessTokenResponse != null)
            {
                this.AccessToken = accessTokenResponse.AccessToken;
                this.lbl.Text =&#34;Token:&#34;+ this.AccessToken+&#34; Screct:&#34;+this.TokenManager.GetTokenSecret(this.AccessToken);
            }
            else if (this.AccessToken == null)
            {
                
                YDWebConsumer.RequestAuthorization(youDao);
            }
        }
    }</code></pre></div></div><div class="rno-markdown-code"><div class="rno-markdown-code-toolbar"><div class="rno-markdown-code-toolbar-info"><div class="rno-markdown-code-toolbar-item is-type"><span class="is-m-hidden">代码语言:</span>javascript</div></div><div class="rno-markdown-code-toolbar-opt"><div class="rno-markdown-code-toolbar-copy"><i class="icon-copy"></i><span class="is-m-hidden">复制</span></div></div></div><div class="developer-code-block"><pre class="prism-token token line-numbers language-javascript"><code class="language-javascript" style="margin-left:0">//TokenManager   

private YDTokenManager TokenManager
{
get
{
var tokenManager = (YDTokenManager)Session["tokenManager"];
if (tokenManager == null)
{
string consumerKey = YDOpenAPI4N.YDAuthBaseInfo.ConsumerKey;
string consumerSecret = YDOpenAPI4N.YDAuthBaseInfo.ConsumerSecret;
if (!string.IsNullOrEmpty(consumerKey))
{
tokenManager = new YDTokenManager(consumerKey, consumerSecret);
Session["tokenManager"] = tokenManager;
}
}

            return tokenManager;
        }
    }</code></pre></div></div><p>跑一下是不是成功了。</p><figure class=""><div class="rno-markdown-img-url" style="text-align:center"><div class="rno-markdown-img-url-inner" style="width:67.48%"><div style="width:100%"><img src="https://cdn.static.attains.cn/app/developer-bbs/upload/1723310964501806871.png" /></div></div></div></figure><figure class=""><div class="rno-markdown-img-url" style="text-align:center"><div class="rno-markdown-img-url-inner" style="width:67.48%"><div style="width:100%"><img src="https://cdn.static.attains.cn/app/developer-bbs/upload/1723310964928089186.png" /></div></div></div></figure><p>有了AccessToken跟AccessSecret我们就可以去访问有道云笔记的操作API了:创建,删除,修改笔记了。这部分接下来我也会实现。</p><p>      希望对于需要了解OAUTH跟DOTNETOPENAUTH的同学能有帮助。</p>