2012-09-27 46 views
29

我從繼承System.Web.Http.AuthorizeAttribute創建自定義授權/認證例程以滿足使用ASP.NET開發的Web應用程序的一些不尋常要求MVC 4.這增加了用於Web客戶端Ajax調用的Web API的安全性。要求是:如何自定義ASP.NET Web API AuthorizeAttribute以獲取異常要求

  1. 用戶必須每次進行交易的時間來驗證 別人還沒有走到工作站後有人 登錄走開了登錄。
  2. 在編程時無法將角色分配給Web服務方法。 必須在運行時分配它們,以便管理員可以配置此項。這些信息存儲在系統數據庫中。

網頁客戶端是一個single page application (SPA)如此典型的窗體身份驗證不工作這麼好,但我想重用盡可能多的ASP.NET安全框架,因爲我能滿足要求。定製AuthorizeAttribute對於確定哪些角色與Web服務方法關聯需求2非常有用。我接受三個參數,應用程序名稱,資源名稱和操作以確定哪些角色與方法關聯。

public class DoThisController : ApiController 
{ 
    [Authorize(Application = "MyApp", Resource = "DoThis", Operation = "read")] 
    public string GetData() 
    { 
     return "We did this."; 
    } 
} 

我重寫OnAuthorization方法來獲得角色和驗證用戶。由於用戶必須針對每個事務進行身份驗證,因此我通過在同一步驟中執行身份驗證和授權來減少來回的喋喋不休。我通過使用基本身份驗證從Web客戶端獲取用戶憑據,該身份驗證將HTTP頭中的加密憑據傳遞給用戶。所以我OnAuthorization方法是這樣的:

public override void OnAuthorization(HttpActionContext actionContext) 
{ 

    string username; 
    string password; 
    if (GetUserNameAndPassword(actionContext, out username, out password)) 
    { 
     if (Membership.ValidateUser(username, password)) 
     { 
      FormsAuthentication.SetAuthCookie(username, false); 
      base.Roles = GetResourceOperationRoles(); 
     } 
     else 
     { 
      FormsAuthentication.SignOut(); 
      base.Roles = ""; 
     } 
    } 
    else 
    { 
     FormsAuthentication.SignOut(); 
     base.Roles = ""; 
    } 
    base.OnAuthorization(actionContext); 
} 

GetUserNameAndPassword檢索來自HTTP標頭的憑據。然後我使用Membership.ValidateUser驗證憑據。我有一個自定義成員資格提供程序和角色提供程序插入到一個自定義數據庫。如果用戶通過身份驗證,那麼我將檢索資源和操作的角色。從那裏我使用基地OnAuthorization完成授權過程。這是它破裂的地方。

如果用戶通過身份驗證,我使用標準表單身份驗證方法在(FormsAuthentication.SetAuthCookie)中記錄用戶,如果他們失敗,我將它們註銷(FormsAuthentication.SignOut)。但問題似乎是基地OnAuthorization類無權訪問本金已更新,以便IsAuthenticated設置爲正確的值。它總是落後一步。我的猜測是,它正在使用一些緩存的值,直到訪問Web客戶端時纔會更新。

因此,所有這一切導致了我的具體問題是,有另一種方式來設置IsAuthenticated爲正確的值當前主要不使用cookies嗎?在我看來,Cookie並不適用於我必須每次驗證身份的特定場景。我知道IsAuthenticated原因未設置爲正確的值我也覆蓋HandleUnauthorizedRequest方法是:

protected override void HandleUnauthorizedRequest(HttpActionContext filterContext) 
{ 
    if (((System.Web.HttpContext.Current.User).Identity).IsAuthenticated) 
    { 
     filterContext.Response = new HttpResponseMessage(System.Net.HttpStatusCode.Forbidden); 
    } 
    else 
    { 
     base.HandleUnauthorizedRequest(filterContext); 
    } 
} 

這使得如果出現故障,我回去的禁止狀態代碼的Web客戶端是因爲授權而不是身份驗證,因此可以做出相應的響應。

那麼,什麼是設置IsAuthenticated在這種情況下當前原則的正確方法?

回答

33

我的方案的最佳解決方案似乎完全繞過基地OnAuthorization。由於每次cookie和緩存原則都不夠用,因此我必須進行身份驗證。因此,這裏是我想出了一個解決方案:

public override void OnAuthorization(HttpActionContext actionContext) 
{ 
    string username; 
    string password; 

    if (GetUserNameAndPassword(actionContext, out username, out password)) 
    { 
     if (Membership.ValidateUser(username, password)) 
     { 
      if (!isUserAuthorized(username)) 
       actionContext.Response = 
        new HttpResponseMessage(System.Net.HttpStatusCode.Forbidden); 
     } 
     else 
     { 
      actionContext.Response = 
       new HttpResponseMessage(System.Net.HttpStatusCode.Unauthorized); 
     } 
    } 
    else 
    { 
     actionContext.Response = 
      new HttpResponseMessage(System.Net.HttpStatusCode.BadRequest); 
    } 
} 

我開發我自己的方法來驗證稱爲角色isUserAuthorized既然它會檢查當前原則,我不使用基本OnAuthorization更多看看它是否isAuthenticatedIsAuthenticated只允許獲取所以我不知道如何設置它,我似乎不需要目前的原則。測試了這一點,它工作正常。

如果任何人有更好的解決方案或可以看到這個這個問題的任何問題,仍然感興趣。

+0

你可以請分享方法正文GetUserNameAndPassword(actionContext,出的用戶名,出密碼)? –

+3

@VijayBalkawade - 此代碼現在是SimpleSecurity開源項目的一部分。您可以在這裏獲取GetUserNameAndPassword的完整源代碼。 https://simplesecurity.codeplex.com/SourceControl/latest#SimpleSecurity.Filters/BasicAuthorizeAttribute.cs –

+0

由於Codeplex即將關閉,我已將[GetUserNameAndPassword'複製到Pastebin](https://pastebin.com/Yq86aNjL)。 –

23

要添加到已經接受的答案:檢查當前源代碼(aspnetwebstack.codeplex.com)System.Web.Http.AuthorizeAttribute,它看起來像文檔已過時。基地OnAuthorization()只是調用/檢查私人靜態SkipAuthorization()(它只是檢查是否在上下文中使用AllowAnonymousAttribute繞過驗證檢查的其餘部分)。然後,如果不跳過,OnAuthorization()呼叫公衆IsAuthorized(),如果該呼叫失敗,它然後調用受保護的虛擬HandleUnauthorizedRequest()。而這一切,它...

public override void OnAuthorization(HttpActionContext actionContext) 
{ 
    if (actionContext == null) 
    { 
     throw Error.ArgumentNull("actionContext"); 
    } 

    if (SkipAuthorization(actionContext)) 
    { 
     return; 
    } 

    if (!IsAuthorized(actionContext)) 
    { 
     HandleUnauthorizedRequest(actionContext); 
    } 
} 

IsAuthorized()來看,這其中的原理是對角色和用戶檢查。所以,覆蓋IsAuthorized()與你上面的而不是OnAuthorization()將是路要走。然後再次,您仍然必須重寫OnAuthorization()HandleUnauthorizedRequest(),以決定何時返回401與403響應。

1

要增加Kevin的絕對正確答案,我想說我可以稍微修改它以利用響應對象的現有.NET框架路徑來確保框架(或其他消費者)中的下游代碼,不會受到某些無法預測的奇怪特質的負面影響。

具體而言,這意味着使用此代碼:

actionContext.Response = actionContext.ControllerContext.Request.CreateErrorResponse(HttpStatusCode.Unauthorized, REQUEST_NOT_AUTHORIZED); 

而非:

actionContext.Response = new HttpResponseMessage(System.Net.HttpStatusCode.Unauthorized); 

哪裏REQUEST_NOT_AUTHORIZED是:

private const string REQUEST_NOT_AUTHORIZED = "Authorization has been denied for this request."; 

我拉,從在所述SRResources.RequestNotAuthorized定義string。 NET框架。

偉大的答案凱文!我以同樣的方式實現了我的目標,因爲在基類中執行OnAuthorization毫無意義,因爲我正在驗證HTTP頭,這是我們的應用程序定製的,並且實際上並不想檢查委託人,因爲沒有一個。

相關問題