一邊享受着鄉村的寧靜,一邊寫着博客,也是一種愜意。
喜歡解決問題后寫一篇博客。通過文字表達出來,會加深自己的理解,還經常會有新的收獲,甚至會找到更好的解決方法。同時,還能分享給別人。一舉多得,何樂而不為呢?
這次要解決的問題是如何在用戶注冊時驗證用戶的郵箱?
通常的解決方法是給用戶的郵箱發一封激活郵件。但這個方法有以下幾個問題:
- 從發出郵件至收到郵件,可能會有延時。
- 郵件可能會被當作垃圾郵件處理
- 用戶可能會填錯了郵箱,更糟糕的情況是用戶不知道自己填錯了郵箱。
這次要解決的問題的更具體的場景是如何驗證用戶的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,這里必須要轉換為HttpWebRequest var 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/OAuth2WebServer var 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_token var 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); } }
完整代碼下載