閱讀704 返回首頁    go 阿裏雲 go 技術社區[雲棲]


C# 網絡編程之豆瓣OAuth2.0認證詳解和遇到的各種問題及解決

        最近在幫人弄一個豆瓣API應用,在豆瓣的OAuth2.0認證過程中遇到了各種問題,同時自己需要一個個的嚐試與解決,最終完成了豆瓣API的訪問.作者這裏就不再吐槽豆瓣的認證文檔了,畢竟人家也不容易.但是作者發現關於豆瓣OAuth認證過程的文章非常之少,所以想詳細寫這樣一篇文章方便後麵要做同樣東西的人閱讀.希望文章對大家有所幫助,尤其是想做豆瓣API開發的初學者.
        (文章中藍色字表示官方文檔引用,紅色字是可能遇到問題及注意,黑色字是作者敘述)

一.誤區OAuth1.0認證過程

       你遇到的第一個問題可能就是還在閱讀"豆瓣 API OAuth認證"這篇官方文檔,並且在瘋狂的嚐試.我就是這樣花費了很長時間研究了OAuth1.0認證過程,但總是錯誤,因為他已經過時了.你需要使用OAuth2.0完成認證過程.閱讀"使用OAuth2.0訪問豆瓣API"官方文檔.
       
但是OAuth1.0提供的思想還是可以的(安慰自己),而且很多關於豆瓣博客文檔都是基於1.0的認證過程,其實已經過時.這裏也簡單講講它的基本過程和原理:(OAuth原理過去Auth1.0認證成果案例)
        它主要是通過Google-OAuth項目提供的C#語言的OAuth庫,在自定義OAuth類中有計算簽名值oauth_signature方法,簽名方法HMAC-SHA1,還有計算時間戳oauth_timestamp等方法,然後在參照DoubanOAuthBasicSample項目完成它的認證流程,主要方法是getRequestToken、authorization和getAccessToken.
        豆瓣OAuth1.0官方文檔給出的具體四個步驟:獲取未授權的Request Token、請求用戶授權Request Token、使用授權後的Request Token換取Access Token、使用 Access Token 訪問或修改受保護資源.具體豆瓣 OAuth1.0認證代碼:
OAuth類DoubanOAuthBasicSample
        其實,你隻要知道它已經過時,不要在使用該方法即可.下麵才是我想講述的具體如何通過C#程序完成豆瓣的OAuth認證並訪問需要授權的數據.

二.獲取autorization_code

      首先在你需要參考的具體豆瓣官方文檔就是:https://developers.douban.com/wiki/?title=oauth2
        使用OAuth2.0流程具體如下:(官方文檔)
        1.應用向豆瓣請求授權
        2.豆瓣為用戶顯示一個授權頁麵,用戶在此頁麵確認是否同意授權
        3.如果用戶同意授權,應用匯獲取到一個訪問令牌(access_token),通過此令牌用戶可以訪問授權數據
        4.如果訪問需要授權的Api,請使用https協議,加上access_token的Header.
        下麵詳細講解
        首先你需要申請API Key,在完成申請過程中你需要注意3個值:API Key\Secret\回調地址.後麵的程序需要應用,當使用的時候我會詳細介紹.由於我是桌麵客戶端應用,授權流程為native-application flow.
        根據它的具體流程我設計的界麵如下圖所示:

      獲取authorization_code步驟(官方文檔)
      通過在瀏覽器中訪問下麵地址,來引導用戶授權並獲取authorization_code.
      
https://www.douban.com/service/auth2/auth
      
參數:
      
client_id  必選參數,應用的唯一標識,對應於APIKey
      
redirect_uri  必選參數,用戶授權完成後的回調地址,通過回調地址獲得用戶的授權結果.與注冊一致.
      
response_type  必選參數,此值可以為code或者token.在本流程中此值為code
      
scope  可選參數,申請權限的範圍,如果不填,則使用缺省的scope.如果申請多個scope,使用逗號分隔
      
state  可選參數,用來維護請求和回調狀態的附加字符串,授權完成回調時會附加此參數,應用根據此字符串來判斷上下文關係.
      注意:此請求必須是HTTP GET方式
      返回結果:
      當用戶拒絕授權時,瀏覽器會重定向到redirect_uri,並附加錯誤信息
      https://www.example.com/back?error=access_denied
      當用戶同意授權時,瀏覽器會重定向到redirect_uri,並附加autorization_code
      https://
www.example.com/back?code=9b73a4248
      首先,你需要定義變量如下,這些變量都是我們需要使用的.其中APIkey\secret\回調地址填寫你自己的,下麵的被我改過不是源數據(擔心丟失)

#region 定義變量        
//申請的API Key
public string apiKey = "00489f145c2576bd00d9dd3d147064";
//申請的API 密鑰
public string apiKeySecret = "72c36131ace8ea";
//申請的回調地址 我的應用URL        
public string myurl = "https://www.baidu.com/";        
//獲取authorization_code
public Uri GetAuthorizationCode = new Uri("https://www.douban.com/service/auth2/auth");
//獲取access_token
public Uri GetAccessToken = new Uri("https://www.douban.com/service/auth2/token");
//瀏覽器返回autorization_code
public string autorizationCode = "";
//AccessToken授權成功後返回的Json數據
public string accessToken = "";
public string userName = "";
public string userId = "";
public string expiresIn = "";
public string refreshToken = "";
#endregion

        然後,點擊button1(瀏覽)按鈕,同時設計視圖中為webBrowser1控件添加DocumentCompleted事件(右鍵->屬性->'閃電圖標'事件).同時引用命名空間:using System.Web;\using System.Net;\using System.IO;

#region 第一步 瀏覽 獲取authorization_code
//點擊"瀏覽"顯示豆瓣登錄界麵
private void button1_Click(object sender, EventArgs e)
{
    //獲取authorization_code構造URL
    StringBuilder url = new StringBuilder(GetAuthorizationCode.ToString()); //可變字符串
    //追加組合格式字符串
    url.AppendFormat("?client_id={0}&", apiKey);
    url.AppendFormat("redirect_uri={0}&", myurl);
    url.AppendFormat("response_type={0}", "code");
    //url.AppendFormat("scope={0}", "scope=shuo_basic_r,shuo_basic_w");
    //顯示輸入URL
    textBox1.Text = url.ToString();
    string Input = url.ToString();
    //將指定URL加載到WebBrowser控件中
    webBrowser1.Navigate(Input);
}

//文件加載完成後發生事件
private void webBrowser1_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
{
    //獲取當前文檔URL
    string reUrl = this.webBrowser1.Url.ToString();
    textBox1.Text = reUrl;
    //獲取https://www.example.com/back?code=9b73a4248中code值
    if (!string.IsNullOrEmpty(reUrl))
    {
        //獲取問號後麵字符串
        string LastUrl = reUrl.Substring(reUrl.LastIndexOf("?") + 1, (reUrl.Length - reUrl.LastIndexOf("?") - 1));               
        //根據參數間的&號獲取參數數組 可以獲取多個參數此時隻需一個code
        string[] urlParam = LastUrl.Split('&');
        foreach (string s in urlParam)
        {
            //將參數名與參數值賦值給數組 value[0]參數名稱 value[1]參數值
            string[] value = s.Split('=');
            //MessageBox.Show("參數名稱為:" + value[0] + " 參數值為:" + value[1]);
            if (value[0] == "code")
            {
                autorizationCode = value[1];
            }
        }
    }
    //輸出獲取code值
    if (autorizationCode != "") MessageBox.Show(autorizationCode);
}
#endregion

        運行結果如下圖所示,填寫完用戶郵箱和密碼,點擊授權後獲取autorization_code如下:

        這裏你需要注意的有一下幾點:
        1.為什麼我的回調地址設置為"https://www.baidu.com/
"?
        
最初我設置的回調地址為自定義的一個網址,但是輸入郵箱和密碼後點擊授權,總是HTTP 400錯誤,啥都不能獲取.使用IE瀏覽器也是一樣,但是google能獲取.後來經過同學提醒(他做騰訊API,資料多,官方文檔規範)可能回調地址需要能訪問的網址,所以改成了百度,並且成功獲取.因為這一步隻需要獲取code值,網址並不關鍵.(可能也有沒考慮到的地方)
       
2.你需要知道該程序中必須使用GET方法=瀏覽可以直接訪問.(除設置header,後麵講述)
        在程序中我是直接調用webBrowser1.Navigate(Input);函數訪問的,也可以使用GET方法訪問.GET和POST的區別是你需要知道的,簡單來說就是GET後麵給的網址參數顯示,而POST是隱式的.
       
程序和文檔中說道的采用GET的方法,並帶有參數的都是這樣連接的.url+?+參數=參數值&參數=參數值...正如獲取code中的URL,它通過瀏覽器是可以直接訪問的.
       
https://www.douban.com/service/auth2/auth?client_id=00489f145c2576bd00d9dd3d147064&redirect_uri=https://www.baidu.com/&response_type=code
        3.你可能會遇到的錯誤是113 缺少參數required_parameter_is_missing.
       
此時你在設置url時,通過StringBuilder可變字符串增加參數時,需要把所有的必須的參數填寫.同時,注意你的&和?是否正確填寫.你可以參考的官方文檔錯誤報告如下:
       
https://developers.douban.com/wiki/?title=oauth2
        https://developers.douban.com/wiki/?title=api_v2
        此時,你的第一步獲取code已經完成,接下來是獲取access_token的過程.

三.獲取access_token

        官方文檔接受的獲取access_token如下:
       
https://www.douban.com/service/auth2/token
       
參數:
       client_id  必選參數,應用的唯一標識,對應於APIKey
        client_sercet 必選參數,應用的唯一標識,對應於豆瓣secret
        redirect_url 必選參數,用戶授權後的回調地址
        grant_type 必選參數,此值可為authorization_code或者refresh_token,此時為authorization_code
        code 必選參數,上一步中獲取的authorization_code
       
注意:此請求必須是HTTP POST方式
        返回結果為Json格式數據如下圖所示

//獲取的數據裝換為Json格式 此時返回的json格式的數據
{
 "access_token":"0e63c03dfb66c4172b2b40b9f2344c",
 "douban_user_name":"Eastmount",
 "douban_user_id":"57279898",
 "expires_in":604800,
 "refresh_token":"84406d40cc58e0ae8cc147c2650aa2",
}   

        其中你需要注意的是四個地方:
        1.如何使用POST方法發送請求獲取應答,這再也不是通過瀏覽器就能直接訪問的問題.
        2.獲取的JSON格式數據如何通過C#解析.
        3.可能你的訪問不成功,你需要在"我的應用"中添加測試用戶.看看自己的是否添加.
        4.你可能遇到101 錯誤的請求方法,invalid_request_method: GET因為你需要采用POST方法.
        
我講解如何解析JSON格式的數據,這裏參照很多人可能都使用過的方法.
        你需要下載Newtonsoft.Json.dll文件並添加引用它,參考百度"
C#使用json字符串"方法,最好先把該dll放置到C:\WINDOWS\Microsoft.NET\Framework文件夾中,裏麵有很多dll(我的程序是VS2012 .NET4.5).
        在"解決方案"中鼠標右鍵引用->添加引用->瀏覽->添加該dll.然後添加命名空間using Newtonsoft.Json.Linq;將POST方法獲取應答string轉換為JSON格式轉換賦值即可.具體代碼如下,同時POST方法發送HTTP請求我是通過自定義函數sendMessage實現的.加載button2(授權)按鈕事件.

#region 第二步 授權 獲取access_token
private void button2_Click(object sender, EventArgs e)
{
    //顯示輸入URL
    textBox1.Text = GetAccessToken.ToString();
    //獲取access_token構造POST參數
    StringBuilder url = new StringBuilder(""); // 可變字符串
    //追加組合格式字符串
    url.AppendFormat("client_id={0}&", apiKey);
    url.AppendFormat("client_secret={0}&", apiKeySecret);
    url.AppendFormat("redirect_uri={0}&", myurl);
    url.AppendFormat("grant_type={0}&", "authorization_code");
    url.AppendFormat("code={0}", autorizationCode);
    //獲取POST提交數據返回內容
    string AccessContent = sendMessage(GetAccessToken.ToString(), url.ToString());
    webBrowser1.DocumentText = AccessContent;
    //獲取的數據裝換為Json格式 此時返回的json格式的數據
    JObject obj = JObject.Parse(AccessContent);
    accessToken = (string)obj["access_token"];   //access_token
    userName = (string)obj["douban_user_name"];  //豆瓣用戶名
    userId = (string)obj["douban_user_id"];      //用戶id
    expiresIn = (string)obj["expires_in"];       //生命周期 604800秒=7天
    refreshToken = (string)obj["refresh_token"]; //刷新令牌
    MessageBox.Show(accessToken + "\n" + userName + "\n" + 
        userId + "\n" + expiresIn + "\n" + refreshToken);
}

//發送消息Post方法
public static string sendMessage(string strUrl, string PostStr)
{
    try
    {
        //設置消息頭
        CookieContainer objCookieContainer = null;
        HttpWebRequest request = (HttpWebRequest)WebRequest.Create(strUrl);
        request.Method = "Post";
        request.ContentType = "application/x-www-form-urlencoded";
        request.Referer = strUrl;
        if (objCookieContainer == null)
            objCookieContainer = new CookieContainer();

        request.CookieContainer = objCookieContainer;
        byte[] byteData = Encoding.UTF8.GetBytes(PostStr.ToString().TrimEnd('&'));
        request.ContentLength = byteData.Length;
        using (Stream reqStream = request.GetRequestStream())
        {
            reqStream.Write(byteData, 0, byteData.Length);
        }
        //Response應答流獲取數據
        string strResponse = "";
        using (HttpWebResponse res = (HttpWebResponse)request.GetResponse())
        {
            objCookieContainer = request.CookieContainer;
            using (Stream resStream = res.GetResponseStream())
            {
                using (StreamReader sr = new StreamReader(resStream, Encoding.UTF8)) //UTF8 
                {
                    strResponse = sr.ReadToEnd();
                }
            }
            // res.Close();
        }
        return strResponse;
    }
    catch (Exception e)
    {
        Console.WriteLine(e.ToString());
        Console.Read();
    }
    return null;
}
#endregion

        運行程序,點擊"瀏覽"授權成果後在點擊"授權"的運行結果如下圖所示:

四.使用access_token

        通過前麵的步驟我們已經獲取了access_token數據,那麼怎樣使用它呢?當我看到豆瓣給出的使用官方文檔如下時:

curl "https://api.douban.com/v2/user/~me"
-H "Authorization: Bearer a14afef0f66fcffce3e0fcd2e34f6ff4"

        我很頭痛啊!那麼,怎樣通過C#實現使用access_token訪問要授權的數據呢?
       
GET https://api.douban.com/v2/user/~me
       
該URL是獲取當前授權用戶信息的,需要必須先進行API認證授權,返回的是當前授權用戶信息.因此,使用它完全可以驗證授權成果後的操作.具體代碼如下:

#region 第三步 豆瓣訪問授權數據
private void button3_Click(object sender, EventArgs e)
{
    try
    {
        //獲取當前授權用戶信息 需要必須先進行API認證授權,返回當前授權的UserInfo
        string Input = "https://api.douban.com/v2/user/~me";
        textBox1.Text = Input;
        //HttpWebRequest對象實例:該類用於獲取和操作HTTP請求
        var request = (HttpWebRequest)WebRequest.Create(Input); //Create:創建WebRequest對象
        //設置請求方法為GET
        request.Headers.Add("Authorization", "Bearer " + accessToken);
        request.Method = "GET";
        //HttpWebResponse對象實例:該類用於獲取和操作HTTP應答 
        var response = (HttpWebResponse)request.GetResponse(); //GetResponse:獲取答複
        //構造數據流對象實例
        Stream stream = response.GetResponseStream(); //GetResponseStream:獲取應答流
        StreamReader sr = new StreamReader(stream); //從字節流中讀取字符
        //從流當前位置讀取到末尾並顯示在WebBrower控件中 
        string content = sr.ReadToEnd();
        webBrowser1.DocumentText = content;
        //關閉響應流
        stream.Close();
        sr.Close();
        response.Close();                      
    }
    catch (Exception msg) //異常處理
    {
        MessageBox.Show(msg.Message);
    }
}
#endregion

        運行結果如下圖所示:

        不要以為這簡單的幾句代碼就很容易實現了訪問數據,其實你需要注意一下幾點:
        1.你可能會遇到1000錯誤 需要權限need_permission?
        那時你需要在訪問時設置header,添加-H "Authorization: Bearer a14afef0f66fcffce3e0fcd2e34f6ff4".采用C#設置標題頭的具體代碼如下:
        request.Headers.Add("Authorization", "Bearer " + accessToken);
       
其中你需要注意Bearer後麵的空格.而且有同學說他在使用網盤認證時,獲取的方法有兩種,一種是設置header,一種是在URL後麵加上?access_token=值即可.但我測試了下豆瓣隻有設置header可以.
        2.認證過程已經詳細敘述了,增刪改查其他數據影片信息、用戶信息、評論、收藏、關注等方法都類似,感興趣的可以自己完成.它的具體設置參數參照豆瓣文檔:https://developers.douban.com/wiki/?title=api_v2

五.總結

        最後總結下它的具體步驟,其實它就是按照豆瓣的文檔完成的,三個步驟:獲取authorization_code、獲取access_token和使用access_token.你也不能說豆瓣文檔講得不好,其實實質東西它都講述清除了.需要的隻是你自己的探索,可能剛接觸比較新鮮和難,但其實完成後就發現它很簡單.
        希望該文章對大家有所幫助,尤其是想做豆瓣API開發的並且使用C#的,這方麵資料比較少,基本都是php和java的.更希望同學能從該文章中學到一下幾個東西:
        1.如何通過官方文檔完成一個東西,可能遇到的問題都需要自己解決,而不是複製粘貼.
        2.如何使用C#網絡編程GET和POST兩種方法HTTP請求並獲取應答.
        3.如何使用C#解析Json格式的數據.
        4.如何使用OAuth認證API開發,這方麵騰訊、新浪等比較完善.
        最後也感謝豆瓣網帶給我很多知識,推薦程序員也看看文學書籍,如《文學回憶錄》和《季羨林 清華園日記》,生活不隻有編程啊.如果有錯誤或不足之處,還請海涵.
        源代碼免費下載地址:https://download.csdn.net/detail/eastmount/7399075
        (By:Eastmount 2014-5-25 下午6點 原創CSDNhttps://blog.csdn.net/eastmount/)
 

最後更新:2017-04-03 08:26:14

  上一篇:go 常用WebService接口
  下一篇:go C連接MySQL數據庫開發之Xcode環境配置及測試