但前提是必須由User同意授權,才能依憑證抓取相對應的資料。
本篇用簡單的方法來實現OAuth2授權並獲取GMail信箱(範例框架為webform,實現方式主要用Http來撰寫)
官方說明文件:
https://developers.google.com/identity/protocols/OAuth2
前置動作:
1.先到Google API Console 新建專案(https://console.developers.google.com/apis/credentials)
2.並設定OAuth 2.0的憑證和OAuth同意畫面
待會程式要做的步驟:
1.呼叫Google提供的 Authorize頁面
2.使用者按下同意後,接收回傳的Authorize code
3.用得到Authorize code和Goole來交換Access Token和Refresh Token
(Refresh Token非必定回傳,但是需要離線授權時會用到,因為Access Token只有1hr的效力)
4.用Access Token來呼叫Google API來獲取要求的User資料
1.創立空白頁後,在畫面上放一個Button(此按鈕作為登入按鈕),
並在Click事件上撰寫下面程式碼
//登入按鈕事件
protected void Btn_GoogleLogIn_Click(object sender, EventArgs e)
{
//發送請求token 來 得到授權碼(Authorization code)
//Authorize網址
string StrUrl = "https://accounts.google.com/o/oauth2/auth";
//Get參數
StringBuilder StrParam= new StringBuilder();
//client_id(Google API Console 中可以找到 新建立專案的編號)
StrParam.Append("client_id=760...j8f.apps.googleusercontent.com&");
//redirect_uri(Google API Console 中設定CallBack的頁面)
StrParam.Append("redirect_uri=http://localhost:55036/GoogleCallBack.aspx&");
//response_type(回傳 Authorization code)
StrParam.Append("response_type=code&");
//要求授權scope(這邊是用 Google OAuth2 API 底下 Scopes , 目的是要抓取使用者信箱)
StrParam.Append("scope=https://www.googleapis.com/auth/userinfo.email&");
//access_type(online或offline 選擇離線,如此才會回傳refresh token)
StrParam.Append("access_type=offline&");
//state 此值會隨著CallBack回來(可塞入Session token或者一些驗證機制,防止跨站請求偽造)這邊先不用
StrParam.Append("state=&");
//連結網址
Response.Redirect(StrUrl +"?" + StrParam.ToString());
}
以上網址最後會導入到如下圖頁面
2.創立對應CallBack回來的頁面GoogleCallBack.aspx,並加入下面程式碼
畫面上放一個Label用來顯示錯誤訊息
protected void Page_Load(object sender, EventArgs e)
{
//接收Google CallBack回來的參數(Get形式)
//Google回傳的錯誤
string StrErr = Request.QueryString["error"] == null ? "" : Request.QueryString["error"];
//有錯誤的情況
if (StrErr.Length > 0)
{
//顯示在畫面上,並且不繼續處理
lbl_Msg.Text = StrErr;
return;
}
//Authorize code
string StrAuthCode = Request.QueryString["code"] == null ? "" : Request.QueryString["code"];
if (StrAuthCode.Length == 0)
{
lbl_Msg.Text = "沒有獲取授權碼!!";
return;
}
//用Authorize code 去交換 Access token 及 Refresh token
string StrAccToken = "";
string StrRefToken = "";
StrErr = AuthCodeChgToken(StrAuthCode, ref StrAccToken, ref StrRefToken);
//有錯誤的情況
if (StrErr.Length > 0)
{
//顯示在畫面上,並且不繼續處理
lbl_Msg.Text = StrErr;
return;
}
//呼叫API用 Access token 去得到 GMail
string StrGMail = "";
StrErr = AccTokenChgGMail(StrAccToken, ref StrGMail);
//有錯誤的情況
if (StrErr.Length > 0)
{
//顯示在畫面上,並且不繼續處理
lbl_Msg.Text = StrErr;
return;
}
//成功得到 GMail及Refresh token後 可以存在資料庫中,
//之後可以直接用Refresh token去得到Access token, 不用再經過授權
StrErr = SaveSQL(StrGMail, StrRefToken);
//有錯誤的情況
if (StrErr.Length > 0)
{
//顯示在畫面上,並且不繼續處理
lbl_Msg.Text = StrErr;
return;
}
}
/// <summary>
/// 用Authorize code 去得到 Access token 及 Refresh token
/// </summary>
/// <param name="AuthCode">Authorize code</param>
/// <param name="AccToken">Access token</param>
/// <param name="RefToken">Refresh token</param>
/// <returns>錯誤訊息</returns>
private string AuthCodeChgToken(string AuthCode, ref string AccToken, ref string RefToken)
{
//Token網址
string StrUrl = "https://oauth2.googleapis.com/token";
//Post參數
StringBuilder StrParam = new StringBuilder();
//Authorize code
StrParam.Append("code=" + AuthCode + "&");
//client_id(Google API Console 中可以找到 此專案的編號)
StrParam.Append("client_id=760...j8f.apps.googleusercontent.com&");
//client_secret(Google API Console 中可以找到 此專案的密碼)
StrParam.Append("client_secret=dHN...3b&");
//redirect_uri(Google API Console 中設定CallBack的頁面)
StrParam.Append("redirect_uri=http://localhost:55036/GoogleCallBack.aspx&");
//grant_type
StrParam.Append("grant_type=authorization_code&");
//接收回傳內容 Json
string StrReJson = "";
using (WebClient WClient = new WebClient())
{
WClient.Headers.Add("content-type", "application/x-www-form-urlencoded");
try
{
StrReJson = WClient.UploadString(StrUrl, "POST", StrParam.ToString());
}
catch
{
return "獲取Token失敗!";
}
}
//解析JSON
JObject Jobj= JsonConvert.DeserializeObject<JObject>(StrReJson);
try{
AccToken = Jobj["access_token"].ToString();
//只有在初次User授權且請求參數為access_type=offline時才會得到
//可以刪除Cookie來清掉User授權
RefToken = Jobj["refresh_token"].ToString();
}
catch
{
return "解析回傳Token失敗!";
}
return "";
}
/// <summary>
/// 用 Access token 去得到 GMail
/// </summary>
/// <param name="AccToken">Access token</param>
/// <param name="GMail">GMail</param>
/// <returns>錯誤訊息</returns>
private string AccTokenChgGMail(string AccToken, ref string GMail)
{
//API網址
string StrUrl = "https://www.googleapis.com/oauth2/v2/userinfo";
//接收回傳內容 Json
string StrReJson = "";
using (WebClient WClient = new WebClient())
{
WClient.Headers.Add("Authorization","Bearer " + AccToken);
try
{
StrReJson = WClient.DownloadString(StrUrl);
}
catch
{
return "獲取GMail失敗!";
}
}
//解析JSON
JObject Jobj = JsonConvert.DeserializeObject<JObject>(StrReJson);
try
{
GMail = Jobj["email"].ToString();
}
catch
{
return "解析回傳GMail失敗!";
}
return "";
}
/// <summary>
/// 將 Refresh token及GMail 儲存到SQL
/// </summary>
/// <param name="GMail"></param>
/// <param name="RefToken"></param>
/// <returns>錯誤訊息</returns>
private string SaveSQL(string GMail, string RefToken)
{
//StrConn:連線字串自行定義
using (SqlConnection Conn = new SqlConnection(StrConn))
{
//開啟連線
Conn.Open();
string StrSQL = "Insert Into GoogleOAuth2 (Gmail, RefreshToken) Values(@Gmail, @RefreshToken)";
SqlCommand cmd = new SqlCommand(StrSQL, Conn);
cmd.Parameters.AddWithValue("@Gmail", GMail);
cmd.Parameters.AddWithValue("@RefreshToken", RefToken);
try
{
cmd.ExecuteNonQuery();
}
catch
{
//關閉連線
Conn.Close();
return "寫入資料庫失敗!";
}
//關閉連線
Conn.Close();
}
return "";
}
到這邊已經完成大部分內容,事實上這只是初步的運用,
如果有需要做到更困難的應用,建議可以直接用Google提供給各種程式語言的函式庫。
而用Refresh Token來刷新Access Token的部分,我將放到下一篇文章裡
沒有留言:
張貼留言