[VS2010] Visual Studio 2010 與 Windows Azure: 在 AppFabric Access Control 資料庫中快速建立大量帳戶的工具

[VS2010] Visual Studio 2010 與 Windows Azure: 在 AppFabric Access Control 資料庫中快速建立大量帳戶的工具

Windows Azure AppFabric 是 Windows Azure 上的一個 Application Server 開發平台,它具有 Access Control 以及 Service Bus 兩種,其中 Access Control 可以做為一個中繼的驗證提供者,它可以讓開發人員將帳戶資料建在它的資料庫中,並且透過它來進行驗證並授與使用者身分證 (claims),再由服務驗證此 claims 的有效性,若有效則授權使用者權限,因此 AppFabric Access Control 也可以做為一種 SSO 的中繼驗證服務供應者。

Access Control 有提供一個 acm.exe 工具程式以讓開發人員生成 SSO 必要的資訊,但是這個指令列工具使用起來筆者覺得嫌麻煩,而且不利於將帳戶資料大量移轉至 Access Control 資料庫,所幸 Access Control 有提供管理服務 (Management Service),因此開發人員可以利用它來撰寫程式以處理大量的帳戶建立工作。筆者在本文撰寫的程式就是具有這種能力的工具。

首先,讀者必須取得 AppFabric Access Control 的帳戶,並且建立一個 service namespace,若讀者沒有,可以參照 Windows Azure AppFabric SDK 中的 Managing a Windows Azure platform AppFabric Account 一章的說明,取得帳戶並建立一個服務的命名空間,在該命名空間的資訊頁中,會有 Management Service 的 URL 以及取用該服務所需要的 name 與 key。

同時,在存取 Management Service 時,都要先由 Access Control 要一支 token,這個 token 可以由 http://[service-namespace]-mgmt.accesscontrol.windows.net 取得 (同時 wrap_scope 要設定 http://[service-namespace].accesscontrol.windows.net/mgmt/issuers),並且要在每次的 issuer create 動作中,在 HTTP Request 的 Authorization 標頭中,附加 "WRAP access_token=\"[TOKEN]\'" 的訊息,否則會擲出 HTTP 401 (Unauthorized) 或是 HTTP 500 (Internal Server Error) 的訊息。

接著,若要大量建立帳戶,可以利用下列的程式碼(專案型態為 Console Application):

class Program
{
    public static string wrap_token = null;

    static void Main(string[] args)
    {
        for (int i = 11; i <= 15; i++)
        {
            createIssuer("[YOUR_AC_NAME]", "[YOUR_AC_PASSWORD]", "MyIssuer" + i.ToString(), "MyUser" + i.ToString(), "[ACCOUNT_PASSWORD]");
            Console.WriteLine("MyIssuer" + i.ToString() + " was created.");
        }

        Console.ReadLine();
    }

    public static string getManagementToken(string ServiceName, string ServiceManagementKey)
    {
        WebClient client = new WebClient();
        client.BaseAddress = "
https://[service-namespace]-mgmt.accesscontrol.windows.net";
        NameValueCollection values = new NameValueCollection();

        values.Add("wrap_name", ServiceName);
        values.Add("wrap_password", ServiceManagementKey);
        values.Add("wrap_scope", "
https://[service-namespace].accesscontrol.windows.net/mgmt/issuers");

        byte[] acsResponseInBytes = client.UploadValues("WRAPv0.9", values);
        return HttpUtility.UrlDecode(Encoding.UTF8.GetString(acsResponseInBytes)
        .Split('&')
        .Single(value => value.StartsWith("wrap_access_token=", StringComparison.OrdinalIgnoreCase))
        .Split('=')[1]);
    }

    public static bool createIssuer(string ServiceName, string ServiceManagementKey, string DisplayName, string IssuerName, string Password)
    {
        HttpWebRequest request = WebRequest.Create("
https://[service-namespace].accesscontrol.windows.net/mgmt/issuers") as HttpWebRequest;

        if (string.IsNullOrEmpty(wrap_token))
            wrap_token = getManagementToken(ServiceName, ServiceManagementKey);

        string data = string.Format(
            @"<?xml version='1.0' encoding='utf-8'?>
                <Issuer xmlns='
http://schemas.microsoft.com/ws/2009/06/acs/rest/resources'>
                  <DisplayName>{0}</DisplayName>
                  <IssuerName>{1}</IssuerName>
                  <Security>
                    <Algorithm>Symmetric256BitKey</Algorithm>
                    <CurrentKey>{2}</CurrentKey>
                    <PreviousKey>{3}</PreviousKey>
                  </Security>
                </Issuer>", DisplayName, IssuerName, getSHA256Str(Password), getSHA256Str(Password));

        byte[] requestData = Encoding.ASCII.GetBytes(data);

        request.Method = "POST";
        request.Headers.Add("Authorization", string.Format("WRAP access_token=\"{0}\"", wrap_token));
        request.Accept = "application/xml";
        request.ContentType = "application/xml";
        request.ContentLength = requestData.Length;

        Stream s = request.GetRequestStream();
        s.Write(requestData, 0, requestData.Length);
        s.Close();

        Stream responseStream = null;

        try
        {
            HttpWebResponse response = request.GetResponse() as HttpWebResponse;

            responseStream = response.GetResponseStream();

            return true;
        }
        catch (WebException ex)
        {
            HttpWebResponse errorResponse = ex.Response as HttpWebResponse;

            Console.WriteLine("Error: {0}", errorResponse.StatusCode);
            return false;
        }
        finally
        {
            responseStream.Close();
        }
    }

    public static string getSHA256Str(string PasswordStr)
    {
        SHA256Managed algorithm = new SHA256Managed();

        byte[] srcData = Encoding.ASCII.GetBytes(PasswordStr);
        byte[] shaData = algorithm.ComputeHash(srcData);

        algorithm = null;
        return Convert.ToBase64String(shaData);
    }
}

此程式的執行結果為:

image

 

若要驗證帳戶是否建立,則可以用 acm.exe,使用 acm getall issuer 列出所有的帳戶清單:

 

image

 

如何?這樣就可以快速的建立許多的 Access Control 帳戶了。

PS: 建立帳戶需要的 request body 格式,可以由 Windows Azure AppFabric Access Control 的 Management Service 文件中取得。

參考資料:

Windows Azure AppFabric SDK: Access Control