[Asp.Net Identity(四)] Xamarin.iOS 呼叫需要驗證的ASP.Net WebApi

摘要: [Asp.Net Identity] Xamarin.iOS 呼叫需要驗證的ASP.Net WebApi

在設計不管是iOS App或者是Android App的時候,跟你的Backend Server有互動應該是很常碰到的應用。越來越多的企業提供WebApi的服務來取代Web Service。,通常你的WebApi會提供介面讓前端跟後端的資料庫有資料存取的互動。這關係到CRUD的動作,當前端Mobile Device要接收資料的時候可能還好,因為就是對資料庫去做Select的動作。但是當你有資料回寫的時候,那就關係到insert,Update,delete的動作。所以這一篇先來討論最基本的部分,如何整合Asp.Net Identity機制來要求前端使用者必須通過驗證才可以呼叫你的WebApi。這一篇文章就來討論iOS的App要如何去呼叫帶有[Authorize]屬性的WebApi。

這篇文章是參考與翻譯外國作者James的文章,原文出處

http://www.azurefromthetrenches.com/?p=471

 

1. 首先要在Visual Studio裡面建立一個WebApi的專案。在驗證方式的地方選擇[個別使用者帳戶]

 

在這個專案裡面,在Controllers資料夾下找到一個名為ValuesController的API Controller。這是WebApi專案範本所提供的WebApi。在這個類別最上方可以看到有一個Authorize attribute屬性。這是用來保護這個WebApi不可匿名存取。編譯執行這個專案後,去存取這個Api,會得到以下的錯誤訊息。(http://localhost:5287/api/Values)

<Error>
<Message>Authorization has been denied for this request.</Message>
</Error>

 

到目前為止,我們就是希望這個WebApi無法被網路上的匿名使用者存取。

 

2.接下來要存取這個WebApi之前,第一步就是要去註冊一個使用者帳號。

連結到這個專案中的help頁面,可以看到底下的WebApi列表 (for example http://localhost:5287/Help)

,列表中看到一個Api叫做Register,這個Api可以允許匿名存取,他是用來協助前端使用者註冊一個帳號到資料庫裡面。

 

 

把這個專案發佈到Windows Azure上,並且在Windows Azure上面建立一個SQL 資料庫。

 

3. 接下來建立註冊資料的Model

在MVC WebApi專案中,有一個RegisterBindingModel類別,這個類別主要寫入資料到資料庫的DTO類別。另 外在Xamarin.iOS專案中作者也建立了一個RegisterModel類別,這兩個型別結構是一樣的。一個是iOS Device要送出資料的結構,另一個是Webapi Server site要接收資料的結構。原作者是用Xamarin.iOS來執行這個範例,所以他建議把這類型的Model放到Portable Class Library裡面,這樣就可以在Xamarin.iOS或者是Xamarin.Android以及MVC專案中去共用這個類別。

class RegisterModel{
    public string UserName { get; set; }
    public string Password { get; set; }
    public string ConfirmPassword { get; set; }
}

 

在Xamain.ios的專案中,有一個RegisterService Class,這是在iOS程式中用HttpWebRequest來傳送一個註冊資訊給Server site Controller。

class RegisterService
{
    public async Task Register(string username, string password, string confirmPassword)
    {
        RegisterModel model = new RegisterModel
        {
            ConfirmPassword = confirmPassword,
            Password = password,
            UserName = username
        };
 
        HttpWebRequest request = new HttpWebRequest(new Uri(String.Format("{0}api/Account/Register", Constants.BaseAddress)));
        request.Method = "POST";
        request.ContentType = "application/json";
        request.Accept = "application/json";
        string json = JsonConvert.SerializeObject(model);
        byte[] bytes = Encoding.UTF8.GetBytes(json);
        using(Stream stream = await request.GetRequestStreamAsync())
        {
            stream.Write(bytes, 0, bytes.Length);
        }
 
        try
        {
            await request.GetResponseAsync();
            return true;
        }
        catch (Exception ex)
        {
            return false;
        }
    }
}

 

剛剛的程式協助使用者註冊一個帳號,但是要如何實作登入?我們再回頭看到WebApi的Help區塊,裡面並沒有一個Login的方法。接下來就是Owin登場的地方了。開啟在專案範例中App_Start資料夾裡面的Startup.Auth.cs檔案。

 

看到Startup.cs檔案的OAuthOptions區塊。

 

實際上,Owin是一個跑在我們網站中的的OAuth驗證伺服器 ( OAuth authentication server),並且他會針對驗證使用者來設定一個OAuth endpoints。

而Server site的驗證會需要使用者提供一個Token,它用使用者的username與password去驗證辨別使用者本身。後續若有其他的Service需要驗證身份時,這個token會在Http header裡面做傳送的動作。上圖 /Token的區塊是Web Server上的Token end point。除了傳送帳號與密碼外,還需要設定grant_type 為 password。接著在Xamarin.iOS裡面建立LoginService Class,負責送出Endpoint所需要的Form Data。

class LoginService
{
    public async Task Login(string username, string password)
    {
        HttpWebRequest request = new HttpWebRequest(new Uri(String.Format("{0}Token", Constants.BaseAddress)));
        request.Method = "POST";
 
        string postString = String.Format("username={0}&password={1}&grant_type=password",    HttpUtility.HtmlEncode(username), HttpUtility.HtmlEncode(password));
        byte[] bytes = Encoding.UTF8.GetBytes(postString);
        using (Stream requestStream = await request.GetRequestStreamAsync())
        {
            requestStream.Write(bytes, 0, bytes.Length);
        }
 
        try
        {
            HttpWebResponse httpResponse =  (HttpWebResponse)(await request.GetResponseAsync());
            string json;
            using (Stream responseStream = httpResponse.GetResponseStream())
            {
                json = new StreamReader(responseStream).ReadToEnd();
            }
            TokenResponseModel tokenResponse = JsonConvert.DeserializeObject(json);
            return tokenResponse.AccessToken;
        }
        catch (Exception ex)
        {
            throw new SecurityException("Bad credentials", ex);
        }
    }
}

 

登入成功後,OAuth Server會回覆一個Json資料,裡面包含著一個access token以及一些資訊。這邊把這個資訊序列化回TokenResponseModel物件。

class TokenResponseModel
{
    [JsonProperty("access_token")]
    public string AccessToken { get; set; }
 
    [JsonProperty("token_type")]
    public string TokenType { get; set; }
 
    [JsonProperty("expires_in")]
    public int ExpiresIn { get; set; }
 
    [JsonProperty("userName")]
    public string Username { get; set; }
 
    [JsonProperty(".issued")]
    public string IssuedAt { get; set; }
 
    [JsonProperty(".expires")]
    public string ExpiresAt { get; set; }
}

 

現在我們有了伺服器建立與回傳的Token後,後續要連接到任何需要驗證或授權的WebApi,都可以使用這個Token。現在就用這個attempt來連接ValuesController。

在呼叫WebApi的時候,Client端必須在Http Header提供access token。名稱必須被宣告為Authorization,並且Header必須要有“Bearer {token}”的format宣告。

class ValuesService
{
    public async Task GetValues(string accessToken)
    {
        HttpWebRequest request = new HttpWebRequest(new Uri(String.Format("{0}api/Values", Constants.BaseAddress)));
        request.Method = "GET";
        request.Accept = "application/json";
        request.Headers.Add("Authorization", String.Format("Bearer {0}", accessToken));
 
        try
        {
            HttpWebResponse httpResponse = (HttpWebResponse)(await request.GetResponseAsync());
            string json;
            using (Stream responseStream = httpResponse.GetResponseStream())
            {
                json = new StreamReader(responseStream).ReadToEnd();
            }
            List values = JsonConvert.DeserializeObject(json);
            return values;
        }
        catch (Exception ex)
        {
            throw new SecurityException("Bad credentials", ex);
        }
    }
}

 

接著就可以去測試你的這個Xamarin.iOS專案了。

 

 

原作者範例檔案GitHub下載位置: https://github.com/JamesRandall/WebAPI2AuthenticationExample 

 

參考文獻:

How To: Register and Authenticate with Web API 2, OAuth and OWIN

http://www.azurefromthetrenches.com/?p=471

OAuth 2.0 筆記 (1) 世界觀

http://blog.yorkxin.org/posts/2013/09/30/oauth2-1-introduction/