如何針對異常需求自定義 ASP.NET Web API AuthorizeAttribute
我從System.Web.Http.AuthorizeAttribute繼承來創建自定義授權/身份驗證常式,以滿足使用 ASP.NET MVC 4 開發的 Web 應用程序的一些不尋常的要求。這增加了用於來自 Web 的 Ajax 呼叫的 Web API 的安全性客戶。要求是:
- 使用者每次執行交易時都必須登錄,以驗證在某人登錄並離開後其他人沒有走到工作站。
- 不能在程序時將角色分配給 Web 服務方法。必須在執行時分配它們,以便管理員可以配置它。此資訊儲存在系統數據庫中。
Web 客戶端是單頁應用程序 (SPA),因此典型的表單身份驗證不能很好地工作,但我正在嘗試盡可能多地重用 ASP.NET 安全框架來滿足要求。定制的AuthorizeAttribute非常適合要求 2 確定哪些角色與 Web 服務方法相關聯。我接受三個參數,應用程序名稱、資源名稱和操作來確定哪些角色與方法相關聯。
public class DoThisController : ApiController { [Authorize(Application = "MyApp", Resource = "DoThis", Operation = "read")] public string GetData() { return "We did this."; } }我重寫OnAuthorization方法以獲取角色並對使用者進行身份驗證。由於每個事務都必須對使用者進行身份驗證,因此我通過在同一步驟中執行身份驗證和授權來減少來回的喋喋不休。我通過使用在 HTTP 標頭中傳遞加密憑據的基本身份驗證從 Web 客戶端獲取使用者憑據。所以我的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類無權訪問已更新的Principal,因此IsAuthenticated設置為正確的值。它總是落後一步。我的猜測是,它正在使用一些記憶體值,直到與 Web 客戶端進行往返時才會更新。
所以所有這些都引出了我的具體問題,即是否有另一種方法可以在不使用 cookie的情況下將IsAuthenticated設置為目前Principal的正確值?在我看來,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 客戶端返回 Forbidden 狀態程式碼,並且它可以相應地響應。
那麼在這種情況下,為目前Principle設置****IsAuthenticated的正確方法是什麼?
我的方案的最佳解決方案似乎是完全繞過基本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,因為它檢查目前的Principle以查看它是否isAuthenticated。 IsAuthenticated只允許獲取,所以我不確定如何設置它,而且我似乎不需要目前的Principle。對此進行了測試,它工作正常。
如果有人有更好的解決方案或者可以看到這個問題的任何問題,仍然感興趣。