2015-09-05 73 views
9

我正在使用Microsoft OwinASP.NET WebApi來爲我的客戶端應用程序進行身份驗證和授權過程。驗證服務器也由HTTPS進行保護。我讀過幾篇文章關於使用Microsoft Owin,我已經選擇了實現其中之一是: Token Based Authentication using ASP.NET Web API 2, Owin, and Identity爲每種客戶端應用程序發出應用程序ID

有我的項目和實施之間存在一些差異:

  1. 我需要如果請求是由我的應用程序在移動電話上發送的,而不是任何其他設備或工具(如Fiddler),請確定我的客戶端。我認爲其中一個選項可能是通過來自移動應用程序的每個請求發送應用程序ID。但我不知道如何以及在哪裏驗證身份驗證服務器應用程序中的請求。這是註冊用戶非常重要:

    [AllowAnonymous] 
        [Route("Register")] 
        public async Task<IHttpActionResult> Register(UserModel userModel) 
        { 
         if (!ModelState.IsValid) 
         { 
          return BadRequest(ModelState); 
         } 
    
         IdentityResult result = await _repo.RegisterUser(userModel); 
    
         IHttpActionResult errorResult = GetErrorResult(result); 
    
         if (errorResult != null) 
         { 
          return errorResult; 
         } 
    
         return Ok(); 
        } 
    

    我不想讓不可靠的設備,即除了移動應用客戶端,調用此方法。

  2. 我需要讓匿名用戶從網站上購買一些產品,但我不知道如何在沒有進行身份驗證的情況下爲匿名用戶頒發令牌的最佳做法。

回答

6

如果你想找出你的客戶,並授權它可以覆蓋的方法ValidateClientAuthentication

在Taiseer的例子中,你有聯繫,你會發現一些代碼:

public override async Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context) 
{ 
    context.Validated(); 
} 

和一張紙條,上面說:

當你注意到這個類從 類「OAuthAuthorizationServerProvider」,我們」繼承已經重寫了兩個方法 「ValidateClientAuthentication」和「GrantResourceOwnerCredentials」。 第一種方法負責驗證「客戶端」,在我們的 案例中,我們只有一個客戶端,因此我們將始終返回驗證成功的 。

如果你想驗證客戶端,你必須在其中放置一些邏輯。
通常情況下,您會在您的http請求頭中傳遞clientIdclientSecret,以便您可以使用某些數據庫參數驗證客戶端的請求。

public override async Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context) 
{ 
    string clientId = string.Empty; 
    string clientSecret = string.Empty; 

    if (!context.TryGetBasicCredentials(out clientId, out clientSecret)) 
    { 
     context.TryGetFormCredentials(out clientId, out clientSecret); 
    } 

    if (context.ClientId == null) 
    { 
     context.SetError("invalid_client", "Client credentials could not be retrieved through the Authorization header."); 
     context.Rejected(); 

     return; 
    } 

    try 
    { 
     // You're going to check the client's credentials on a database. 
     if (clientId == "MyApp" && clientSecret == "MySecret") 
     { 
      context.Validated(clientId); 
     } 
     else 
     { 
      // Client could not be validated. 
      context.SetError("invalid_client", "Client credentials are invalid."); 
      context.Rejected(); 
     } 
    } 
    catch (Exception ex) 
    { 
     string errorMessage = ex.Message; 
     context.SetError("server_error"); 
     context.Rejected(); 
    } 

    return; 
} 

在樣品上,你會嘗試提取您請求的頭中發送客戶端憑證:

if (!context.TryGetBasicCredentials(out clientId, out clientSecret)) 
{ 
    context.TryGetFormCredentials(out clientId, out clientSecret); 
} 

和驗證他們:

// You're going to check the client's credentials on a database. 
if (clientId == "MyApp" && clientSecret == "MySecret") 
{ 
    context.Validated(clientId); 
} 

如果客戶端發送錯誤的請求標題您需要拒絕請求:

context.SetError("invalid_client", "Client credentials are invalid."); 
context.Rejected(); 

方法ValidateClientAuthenticationGrantResourceOwnerCredentials之前處理。通過這種方式,您可以擴展它並將GrantResourceOwnerCredentials傳遞給您可能需要的額外信息。

在我的應用程序之一,我創建了一個類:

class ApplicationClient 
{ 
    public string Id { get; set; } 
    public string Name { get; set; } 
    public string ClientSecretHash { get; set; } 
    public OAuthGrant AllowedGrant { get; set; } 
    public DateTimeOffset CreatedOn { get; set; } 
} 

我在ValidateClientAuthentication使用我檢查之後確定clientid和祕密都ok:

if (clientId == "MyApp" && clientSecret == "MySecret") 
{ 
    ApplicationClient client = new ApplicationClient(); 
    client.Id = clientId; 
    client.AllowedGrant = OAuthGrant.ResourceOwner; 
    client.ClientSecretHash = new PasswordHasher().HashPassword("MySecret"); 
    client.Name = "My App"; 
    client.CreatedOn = DateTimeOffset.UtcNow; 

    context.OwinContext.Set<ApplicationClient>("oauth:client", client); 

    context.Validated(clientId); 
} 

由於你可以看到這裏

context.OwinContext.Set<ApplicationClient>("oauth:client", client); 

我正在設置一個Owin變量,我可以在以後閱讀。在現在的GrantResourceOwnerCredentials可以讀取的情況下,該變量你需要它:

public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context) 
{ 
    ApplicationClient client = context.OwinContext.Get<ApplicationClient>("oauth:client"); 
    ... 
} 

現在,如果你想獲取承載令牌 - 你要使用的所有的安全API調用 - 你需要編碼您clientIdclientSecret(BASE64),並通過它在請求頭:

與jQuery Ajax請求會是這個樣子:

var clientId = "MyApp"; 
var clientSecret = "MySecret"; 

var authorizationBasic = $.base64.btoa(clientId + ':' + clientSecret); 

$.ajax({ 
     type: 'POST', 
     url: '<your API token validator>', 
     data: { username: 'John', password: 'Smith', grant_type: 'password' }, 
     dataType: "json", 
     contentType: 'application/x-www-form-urlencoded; charset=utf-8', 
     xhrFields: { 
      withCredentials: true 
     }, 
     headers: { 
      'Authorization': 'Basic ' + authorizationBasic 
     }, 
     beforeSend: function (xhr) { 
     }, 
     success: function (result) { 
     var token = result.access_token; 
     }, 
     error: function (req, status, error) { 
      alert(error); 
     } 
}); 

正如你可以看到我還添加了用戶名和密碼 - 與g咆哮型 - 請求中的主體:

data: { username: 'John', password: 'Smith', grant_type: 'password' } 

,使得服務器將能夠驗證所述客戶端(+的clientId clientSecret)和用戶(用戶名+密碼)。

如果申請成功,您應該會得到有效的標記:

oAuth.Token = result.access_token; 

,你可以在一些地方保存用於下列要求。

現在你可以使用此令牌的所有請求的API:

$.ajax({ 
    type: 'GET', 
    url: 'myapi/fetchCustomer/001', 
    data: { }, 
    dataType: "json", 
    headers: { 
    'Authorization': 'Bearer ' + oAuth.Token 
    }, 
    success: function (result) { 
    // your customer is in the result. 
    }, 
    error: function (req, status, error) { 
    alert(error); 
    } 
}); 

你可能要在啓動過程中添加到您的API的另一件事是SuppressDefaultHostAuthentication

config.SuppressDefaultHostAuthentication(); 

這是HttpConfiguration的擴展方法。由於您使用的是持票人標記,因此您想要禁止標準的基於cookie的身份驗證機制。

泰賽爾寫了另一系列的articles值得一讀,他解釋了所有這些事情。

我已經創建了github repo,您可以在其中看到它是如何工作的。
Web API是自託管的,並且有兩個客戶端:jQuery和控制檯應用程序。

+0

這是一個完美的,結構良好的答案!我會測試你的解決方案。謝謝。 –

+0

讓我知道它是怎麼回事。我花了一段時間試圖弄清楚所有這些部分如何組合在一起。這並不容易,尤其是因爲網上沒有很多文檔。 – LeftyX

+0

是的,你是對的我花了兩天時間只是爲了瞭解結構,因爲缺乏文檔。我會報告結果:) –