一邊享受著鄉村的寧靜,一邊寫著部落格,也是一種愜意。
喜歡解決問題後寫一篇部落格。通過文字表達出來,會加深自己的理解,還經常會有新的收穫,甚至會找到更好的解決方案。同時,還能分享給別人。一舉多得,何樂而不為呢?
這次要解決的問題是如何在使用者註冊時驗證使用者的郵箱?
通常的解決方案是給使用者的郵箱發一封啟用郵件。但這個方法有以下幾個問題:
- 從發出郵件至收到郵件,可能會有延時。
- 郵件可能會被當作垃圾郵件處理
- 使用者可能會填錯了郵箱,更糟糕的情況是使用者不知道自己填錯了郵箱。
這次要解決的問題的更具體的情境是如何驗證使用者的gmail郵箱?
我們採用的解決方案是通過OAuth調用Google API擷取使用者的gmail地址。
於是,問題就變為如何在ASP.NET MVC中通過OAuth調用Google API?
必看的兩個文檔:
Using OAuth 2.0 to Access Google APIs
Using OAuth 2.0 for Web Server Applications
我們的OAuth應用情境是Web Server Applications,對應的順序圖表如下:
簡單描述一下整個流程:
- 你在網站上提供一個Google OAuth登入的連結
- 使用者點擊這個連結進入Google登入頁面進行登入
- 使用者登入成功後,會顯示授權頁面。
- 使用者授權成功後,Google會自動重新導向至你的網站頁面,並傳遞authorization code給你。
- 通過這個authorization code,向Google OAuth伺服器請求access_token。
- 拿到access_token之後,調用Google API擷取使用者資訊。
下面是具體的實現步驟:
1. 進入Google APIs Console,建立一個Project,並建立Client ID,如:
得到這些資訊 —— Client ID, Email address, Client secret, Redirect URIs
2. 建立一個空的ASP.NET MVC項目
3. 在web.config中添加相應的appSetting儲存第1步得到的Client ID相關資訊
<appSettings> <add key="ClientID" value=""/> <add key="EmailAddress" value=""/> <add key="ClientSecret" value=""/> <add key="RedirectURI" value=""/> </appSettings>
4. 建立MVC控制器OAuthController,並添加名為GoogleLogin的Action,用於重新導向至Google頁面進行登入。代碼如下:
public class OAuthController : Controller{ public ActionResult GoogleLogin() { var url = "https://accounts.google.com/o/oauth2/auth?"+ "scope={0}&state={1}&redirect_uri={2}&response_type=code&client_id={3}&approval_prompt=force"; //userinfo.email表示擷取使用者的email var scope = HttpUtility.UrlEncode("https://www.googleapis.com/auth/userinfo.email"); //對應於userinfo.email var state = "email"; var redirectUri = HttpUtility.UrlEncode(ConfigurationManager.AppSettings["RedirectURI"]); var cilentId = HttpUtility.UrlEncode(ConfigurationManager.AppSettings["ClientID"]); return Redirect(string.Format(url, scope, state, redirectUri, cilentId)); }}
編譯後,通過瀏覽器訪問,假設網域名稱是passport.cnblogs.cc,訪問網址就是passport.cnblogs.cc/oauth/googlelogin,訪問後,如果你的google帳戶已經處於登入狀態,則直接顯示授權頁面,如:
點擊"Allow access"之後,頁面會被重新導向回你的網站,我們這裡重新導向過來的網址是passport.cnblogs.cc/oauth2callback?state=email&code=4/BSCUqsaY6S5GYk9tFR-45-_UhL4-,查詢參數code的值就是authorization code,接下來就是對這個重新導向網址passport.cnblogs.cc/oauth2callback的處理(這個網址就是第1步中得到的Redirect URIs)。
5. 在Global.asax.cs中添加路由規則,代碼如下:
routes.MapRoute( "oauth2callback", "oauth2callback", new { controller = "OAuth", action = "GoogleCallback", id = UrlParameter.Optional });
6. 在OAuthController中添加名為GoogleCallback的Action
public class OAuthController : Controller{ public ActionResult GoogleCallback() { }}
接下來的操作都在GoogleCallback()中完成。
7. 這一步是關鍵的地方,主要完成兩個操作:
a) 通過authorization code,向Google OAuth伺服器請求access_token(存取權杖,每次調用Google API都需要這個)。
b) 拿到access_token之後,調用Google API擷取使用者資訊(這裡是email)。
主要參考文檔:https://developers.google.com/accounts/docs/OAuth2WebServer
7.1 根據authorization code擷取access_token的代碼流程是:
- 向https://accounts.google.com/o/oauth2/token發送HTTP POST請求,並傳遞相應的參數。
- 擷取伺服器的響應,響應內容的格式時json格式。
- 將json還原序列化為匿名型別的執行個體,並擷取access_token。
代碼如下:
//由於是https,這裡必須要轉換為HttpWebRequestvar webRequest = WebRequest.Create("https://accounts.google.com/o/oauth2/token") as HttpWebRequest;webRequest.Method = "POST";webRequest.ContentType = "application/x-www-form-urlencoded";//參考https://developers.google.com/accounts/docs/OAuth2WebServervar postData = string.Format("code={0}&client_id={1}&client_secret={2}&redirect_uri={3}" + "&grant_type=authorization_code", Request.QueryString["code"], ConfigurationManager.AppSettings["ClientID"], ConfigurationManager.AppSettings["ClientSecret"], ConfigurationManager.AppSettings["RedirectURI"]);//在HTTP POST請求中傳遞參數using (var sw = new StreamWriter(webRequest.GetRequestStream())){ sw.Write(postData);}//發送請求,並擷取伺服器響應var resonseJson = "";using (var response = webRequest.GetResponse()){ using (var sr = new StreamReader(response.GetResponseStream())) { resonseJson = sr.ReadToEnd(); }}//通過Json.NET對伺服器返回的json字串進行還原序列化,得到access_tokenvar accessToken = JsonConvert.DeserializeAnonymousType(resonseJson, new { access_token = "" }).access_token;
7.2 根據access_token讀取使用者資訊的代碼流程是:
- 向https://www.googleapis.com/oauth2/v1/userinfo發送HTTP GET請求,在要求標頭中包含access_token資訊。
- 擷取伺服器的json格式的響應內容,並從中讀取使用者的email資訊。
代碼如下:
webRequest = WebRequest.Create("https://www.googleapis.com/oauth2/v1/userinfo") as HttpWebRequest;webRequest.Method = "GET";webRequest.Headers.Add("Authorization", "Bearer " + accessToken);using (var response = webRequest.GetResponse()){ using (var sr = new StreamReader(response.GetResponseStream())) { return Content(JsonConvert.DeserializeAnonymousType(sr.ReadToEnd(), new { Email = "" }).Email); }}
完整代碼下載
http://files.cnblogs.com/dudu/CNBlogsDemoMvcOAuth.rar