將 AuthSub 與 .NET 用戶端程式庫搭配使用

Google Data API 團隊的 Jeff Fisher
2007 年 8 月

簡介:為什麼 AuthSub 很重要?

Google Data API (簡稱「GData」) 的一大優點,在於開發人員能夠打造出與 Google 服務互動的應用程式。具體來說,這類 API 可讓您存取可在應用程式中使用的私人使用者資料。這些 API 可讓您編寫應用程式來同步處理、匯入、匯出及管理這些資料。儘管 API 提供的功能相當強大,但請務必記得以負責任的方式使用這些功能。由於使用者資料屬於私人資訊,因此您想要透過安全的方式存取這些資料。重要的一環是能夠以安全的方式驗證 Google 伺服器。

假設您有一個優質的新網路應用程式,且想要與儲存在 Google 網路服務中的資料連結。現在,您必須驗證才能存取這項私人資料。為何不單純使用 ClientLogin 之類的簡單方法?雖然這可行,但您將處理更多的私人資料:使用者的登入憑證。ClientLogin 會要求您的應用程式要求使用者的 Google 使用者名稱和密碼。這對在個人用電腦上執行的桌面應用程式而言,這個做法並不適合使用網路應用程式。除了在自己的伺服器上處理這些憑證的責任之外,有些較謹慎的使用者也可能讓您儲存他們的資訊。還有一項使用者常抱持的疑慮,就是他們只想將計劃存取權授予特定的服務 (例如「Google 日曆」中的活動),而不授予其他服務 (例如「Google 文件」) 的存取權。AuthSub 可讓使用者透過 Google 伺服器進行驗證,讓程式只能要求存取,藉此解決這兩個問題。

瞭解 AuthSub 背後的理論後,接下來就要開始寫程式了!在這篇文章中,我決定將簡單明瞭的一切匯集到單一 ASP 網頁中,但您應該能在自己的應用程式中輕鬆運用這裡提到的技巧。

處理驗證

那麼,要在網頁應用程式中實際使用 AuthSub 需要具備哪些條件?首先,GData 用戶端程式庫會提供一些標準匯入項目:

<%@ Import Namespace="Google.GData.Client" %>
<%@ Import Namespace="Google.GData.Extensions" %>
<%@ Import Namespace="System.Net" %>

現在,您需要將使用者傳送到專門的 URL。如此一來,Google 的伺服器便會處理驗證,並將使用者重新導向回您的網站。幸好,您不需要手動產生這個網址,因為我們有幾種方式為您代勞。以下面這段程式碼為例:

authSubUrl = AuthSubUtil.getRequestUrl(target, scope, secure, session);
  • target:這個字串包含您網路應用程式的網址。使用者完成驗證後,系統會將他們重新導向至這裡。
  • scope 這個字串取決於您使用的 API。其對應 GData API 中的其中一個資訊提供。例如,內含使用者所有日曆資訊的資訊提供為「http://www.google.com/calendar/feeds/default/private/full」。
  • 安全:這個布林值會告知伺服器您已向 Google 註冊,並用加密編譯的方式,將您的要求簽署到伺服器。根據預設,這個引數通常是 false,特別是在測試環境中作業時。
  • session:這是另一個布林值,表示您想要「sessiontoken」,而非「一次性使用權杖」。這個引數的角色在稍後會更加清楚。

使用者只要按一下產生的網址,系統就會將他們帶往 Google 帳戶頁面,使用者可以透過這個頁面登入 Google 帳戶。之後,系統會將其重新導向回您在「target」變數中指定的網頁,但使用包含「一次性」權杖的查詢參數「token」。一般來說,這個符記只能使用一次。換句話說,它可以用來對指定的資訊提供執行一項動作。不過,如果您將「session」參數指定為 true,就能交換為「工作階段符記」,可在使用者結束工作階段之前重複使用。您可以選擇下列其中一種操作方式:

String token = Request.QueryString["token"];
Session["token"] = AuthSubUtil.exchangeForSessionToken(token, null).ToString();

您可以在這裡從查詢參數中擷取憑證,並交換以用於「工作階段符記」。然後,您就可以儲存該憑證儲存在 .NET 的自動 Session 陣列中。不過,您也可以選擇將憑證儲存在資料庫中。下一步是使用這組憑證提出經過驗證的要求:

GAuthSubRequestFactory authFactory = new GAuthSubRequestFactory("cl", "My-Cool-Application");
authFactory.Token = (String) Session["token"];
CalendarService service = new CalendarService(authFactory.ApplicationName);
service.RequestFactory = authFactory;

在這個步驟中,您可以設定 CalendarService 物件,透過 AuthSub 進行驗證以與 Google Calendar API 互動。請注意,GAuthSubRequestFactory 建構函式中使用的「cl」是 Google 日曆的服務名稱。如需其他服務名稱的資訊,請參閱 Google Data API 常見問題

安全 (已註冊) AuthSub

如果您選擇註冊網路應用程式,可以在使用 AuthSub 時提升安全性。如此一來,您就能以數位方式簽署程式碼提出的所有要求,這樣一來,除非他人有私密金鑰,否則他人無法使用核發給您的 AuthSub 憑證。第一步是確定您在呼叫 AuthSubUtil.getRequestUrl 時將「secure」引數設為 true,以產生正確的 AuthSub 連結。您需要另外進行兩項程式碼變更:

String token = Request.QueryString["token"];
Session["token"] = AuthSubUtil.exchangeForSessionToken(token, rsaKey).ToString();

...

authFactory.PrivateKey = rsaKey;

首先,請注意 null,而不是將 null 傳送至變數的「rsaKey」。設定與服務的連線時,這個變數也會用來設定 GAuthSubRequestFactory 的屬性。「rsaKey」變數是一個 RSACryptoServiceProvider,對應您在 Google 註冊的 x509 憑證的私密金鑰元件。

產生 RSA 私密金鑰和自行簽署憑證有時並不容易,尤其是 .NET 架構無法解讀以 PEM 格式儲存的金鑰或憑證。下列指令示範如何使用 OpenSSL 工具產生私密金鑰和公開憑證:

openssl req -x509 -nodes -days 365 -newkey rsa:1024 -sha1 -subj \
  '/C=US/ST=CA/L=Mountain View/CN=www.example.com' -keyout \
  test_key.pem -out test_cert.pem

openssl pkcs12 -export -in test_cert.pem -inkey test_key.pem \
  -out test_cert.pfx -name "Testing Certificate"

第一個步驟會產生分別採用「test_key.pem」和「test_cert.pem」的 PEM 格式的私密金鑰和公開 X509 憑證。請注意,憑證的註冊狀態為「www.example.com」,位於美國加州山景城。請將其中的值替換為貴公司的值。「test_cert.pem」檔案包含您要在 AuthSub 註冊頁面提交的資訊。

第二個步驟則會透過您的私密金鑰和憑證產生 PFX 檔案。這個檔案可匯入 .NET 用戶端程式庫,以數位方式簽署對 GData API 發出的要求。以下程式碼顯示如何將私密金鑰從 PFX 檔案匯入網路應用程式:

protected AsymmetricAlgorithm getRsaKey()
{

  X509Certificate2 cert = new X509Certificate2("C:/MyAspSite/test_cert.pfx","");
  RSACryptoServiceProvider privateKey = cert.PrivateKey as RSACryptoServiceProvider;

  return privateKey;
}

用於驗證 API 的程式碼片段時,這個程式碼片段定義的 getRsaKey() 函式可以取代上述的「rsaKey」變數。您應將產生的路徑改成您產生的 PFX 檔案的適當位置。

完整程式碼清單

使用上一個範例所示範的方法,示範如何使用上一節所述的方法。以下範例程式碼是一個簡單的 ASP 網頁,該頁面使用 AuthSub 驗證使用者,然後列印其「Google 日曆」的活動。

<%@ Page Language="C#" AutoEventWireup="true"  CodeFile="Default.aspx.cs" Inherits="_Default" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<%@ Import Namespace="Google.GData.Client" %>
<%@ Import Namespace="Google.GData.Extensions" %>
<%@ Import Namespace="Google.GData.Calendar" %>
<%@ Import Namespace="System.Net" %>

<script runat="server">
    void PrintCalendar() {

        GAuthSubRequestFactory authFactory = new GAuthSubRequestFactory("cl", "TesterApp");
        authFactory.Token = (String) Session["token"];
        CalendarService service = new CalendarService(authFactory.ApplicationName);
        service.RequestFactory = authFactory;

        EventQuery query = new EventQuery();

        query.Uri = new Uri("http://www.google.com/calendar/feeds/default/private/full");

        try
        {
            EventFeed calFeed = service.Query(query);
            foreach (Google.GData.Calendar.EventEntry entry in calFeed.Entries)
            {
                Response.Write("Event: " + entry.Title.Text + "<br/>");
            }
        }
        catch (GDataRequestException gdre)
        {
            HttpWebResponse response = (HttpWebResponse)gdre.Response;
            
            //bad auth token, clear session and refresh the page
            if (response.StatusCode == HttpStatusCode.Unauthorized)
            {
                Session.Clear();
                Response.Redirect(Request.Url.AbsolutePath, true);
            }
            else
            {
                Response.Write("Error processing request: " + gdre.ToString());
            }
        }
    }

</script>


<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
    <title>Test Site</title>
</head>
<body>

    <form id="form1" runat="server">
    <h1>AuthSub Sample Page</h1>
    <div>
    <%
        GotoAuthSubLink.Visible = false;
        
        if (Session["token"] != null)
        {
            PrintCalendar();
        }
        else if (Request.QueryString["token"] != null)
        {
            String token = Request.QueryString["token"];
            Session["token"] = AuthSubUtil.exchangeForSessionToken(token, null).ToString();
            Response.Redirect(Request.Url.AbsolutePath, true);
        }
        else //no auth data, print link
        {
            GotoAuthSubLink.Text = "Login to your Google Account";
            GotoAuthSubLink.Visible = true;
            GotoAuthSubLink.NavigateUrl = AuthSubUtil.getRequestUrl(Request.Url.ToString(),
                "http://www.google.com/calendar/feeds/",false,true);
        }
        
     %>
    <asp:HyperLink ID="GotoAuthSubLink" runat="server"/>

    </div>
    </form>
</body>
</html>

結語

AuthSub 可讓網路應用程式以安全且可控制的方式存取儲存在使用者 Google 帳戶中的資料。使用 .NET 用戶端程式庫可以輕鬆地將 ASP 網站與 Google 服務整合。本文旨在協助您快速上手,但仍建議您參考下列額外資源: