【oAuth 2.0 實作系列】ASP.Net MVC 實作使用 oAuth 2.0 連接 Google API
前言
昨天簡單介紹了 oAuth 2.0 的流程,但是只用文字描述還是沒辦法清楚瞭解 oAuth 2.0 的驗證流程,接下來會介紹如何使用 ASP.Net MVC 來實作用 oAuth 2.0 連結 Google、Facebook、Windows Live 的 API,並且說明一些參數上的差別和驗證的網址。而這一系列第一篇就是連接到 Google API 囉!
實作
首先先參考之前的文章,申請好 Google API 金鑰,並且設定好 Callback 網址(可以先開好專案,編譯後執行取得測試的網址)。
開啟一個新的 ASP.Net MVC3 的專案。
使用 NuGet 加入 JSON.Net 這一個套件,因為後續回傳的資料會是 JSON,透過這一個套件可以方便轉成我們的類別。
建立一個 Controller,本例為 HomeController。
新增一個靜態類別 Utility ,並且新增一個靜態方法 UrlEncode,這邊不用微軟內建的函數是因為微軟在部分字元上編碼有些許不同,因此使用自定的方式來處理避免有所差異導致錯誤。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
public static class Utitity { private const string UnReservedChars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_.~"; /// <summary> /// Url Encoding /// </summary> /// <param name="value"></param> /// <returns></returns> public static string UrlEncode(string value) { StringBuilder result = new StringBuilder(); foreach (char symbol in value) { if (UnReservedChars.IndexOf(symbol) != -1) { result.Append(symbol); } else { result.Append('%' + String.Format("{0:X2}", (int)symbol)); } } return result.ToString(); } } |
接下來新增一個 TokenData 類別,之後用來轉換接收到的 JSON 資料,未來接收到的會有四個參數,但是之後連接 API 我們只會用到 access_token。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
public class TokenData { /// <summary> /// access_token /// </summary> public string access_token { get; set; } /// <summary> /// refresh_token /// </summary> public string refresh_token { get; set; } /// <summary> /// expires_in /// </summary> public string expires_in { get; set; } /// <summary> /// token_type /// </summary> public string token_type { get; set; } } |
接下來開始修改 Controller 囉!首先準備好 const 參數
1 2 3 4 5 6 7 8 9 10 11 12 |
/// <summary> /// 你申請的 client_id /// </summary> private const string client_id = "{client_id}"; /// <summary> /// 你申請的 client_secret /// </summary> private const string client_secret = "{client_secret}"; /// <summary> /// 申請時候設定的回傳網址 /// </summary> private const string redirect_uri = "{redirect_uri}"; |
接下來撰寫三個 Action:index、CallBack、CallAPI
index
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
public ActionResult Index() { // 可在額外加入 approval_prompt=force 參數, // 就不會授權過還會在出現授權畫面 string Url = "https://accounts.google.com/o/oauth2/auth?scope={0}&redirect_uri={1}&response_type={2}&client_id={3}&state={4}"; // https://www.googleapis.com/auth/calendar // https://www.googleapis.com/auth/calendar.readonly // 這兩個是存取 Google Calendar 的 scope 中間用空白做分隔 // UrlEncode 之後再額外用 + 取代 %20 string scope = Utitity.UrlEncode("https://www.googleapis.com/auth/calendar https://www.googleapis.com/auth/calendar.readonly").Replace("%20", "+"); string redirect_uri_encode = Utitity.UrlEncode(redirect_uri); string response_type = "code"; string state = ""; Response.Redirect(string.Format(Url, scope, redirect_uri_encode, response_type, client_id, state)); return null; } |
這邊在網址部分, Google 還多了 approval_prompt 參數,它為非必填,預設值為 auto,會在每次連接到允許授權網址的時候都需要使用者做驗證,可以設定成 force,這樣的話使用者認證過就會自動跳過授權畫面轉到你設定的回傳網址。
Callback
使用者允許授權之後就會轉到一個網址,並且帶 Code 參數,我們在透過這一個參數去取得 AccessToken,正確取得之後就將他記錄在 Session(或是資料庫),然後再轉到 CallAPI 來呼叫 API,這邊不建議把 Token 當做參數傳給 CallAPI ,避免直接暴露給 Client 端。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
public ActionResult CallBack(string Code) { // 沒有接收到參數 if (string.IsNullOrEmpty(Code)) return Content("沒有收到 Code"); string Url = "https://accounts.google.com/o/oauth2/token"; string grant_type = "authorization_code"; string redirect_uri_encode = Utitity.UrlEncode(redirect_uri); string data = "code={0}&client_id={1}&client_secret={2}&redirect_uri={3}&grant_type={4}"; HttpWebRequest request = HttpWebRequest.Create(Url) as HttpWebRequest; string result = null; request.Method = "POST"; // 方法 request.KeepAlive = true; //是否保持連線 request.ContentType = "application/x-www-form-urlencoded"; string param = string.Format(data, Code, client_id, client_secret, redirect_uri_encode, grant_type); byte[] bs = Encoding.ASCII.GetBytes(param); using (Stream reqStream = request.GetRequestStream()) { reqStream.Write(bs, 0, bs.Length); } using (WebResponse response = request.GetResponse()) { StreamReader sr = new StreamReader(response.GetResponseStream()); result = sr.ReadToEnd(); sr.Close(); } TokenData tokenData = JsonConvert.DeserializeObject<TokenData>(result); Session["token"] = tokenData.access_token; // 這邊不建議直接把 Token 當做參數傳給 CallAPI 可以避免 Token 洩漏 return RedirectToAction("CallAPI"); } |
CallAPI
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
public ActionResult CallAPI() { if (Session["token"] == null) return Content("請先取得授權!"); string token = Session["token"] as string; // 取得日曆列表的 API 網址 string Url = "https://www.googleapis.com/calendar/v3/users/me/calendarList?access_token=" + token; HttpWebRequest request = HttpWebRequest.Create(Url) as HttpWebRequest; string result = null; request.Method = "GET"; // 方法 request.KeepAlive = true; //是否保持連線 // 不使用接參數的方式可以在 Head 加上 Authorization,如下一行程式碼 //request.Headers.Add("Authorization", "Bearer " + token); using (WebResponse response = request.GetResponse()) { StreamReader sr = new StreamReader(response.GetResponseStream()); result = sr.ReadToEnd(); sr.Close(); } Response.Write(result); return null; } |
執行程式
程式都寫好之後就是開始測試了,首先就是第一階段的允許授權的畫面
按下允許之後,程式就會回傳到 CallBack 這一個 Action 並且帶 Code 這一個參數,我們在透過這一個參數去取得 Token,取得之後就直接轉去 CallAPI 來使用,就可以取得行事曆列表囉!
結論
透過實作的範例,應該就可以更容易了解到整個 oAuth 的驗證流程了,而其中 API 需要的 Scope 和 API 網址都可以從 Google API 的文件中找到,這邊只使用到 Google Callendar API,如果是別的 API就請讀者自行尋找文件囉!
十二月 1st, 2012 - 06:59
是否能請作者大人百忙中抽空寫一篇關於XUITE
http://api.xuite.net/document/bin/xuite_dev/public/oauth
http://api.xuite.net/document/bin/xuite_dev/public/front/index/id/242
感謝萬分