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