2009-06-09 176 views
24

我有一個關於MVC自定義授權的問題。ASP.NET MVC自定義授權

我有一個網站,我想限制訪問某些頁面,具體取決於他們的組成員資格。現在我已經看到了很多關於如何做到這一點的例子,例如,如果只有一個管理員組和一個用戶組,但沒有任何第三級的例子。

例如,只有公司的用戶才能查看自己公司的訂單(並且每個公司都有自己的管理員等)。這些公司存儲在數據庫中。所以我已經看到了做自定義授權的方法,覆蓋了AuthorizeAttribute上的AuthorizeCore方法,但我不知道如何訪問傳遞給控制器​​的參數以查看用戶是否可以訪問訂單(例如,訂單ID )。

這是甚至是做檢查的最好的地方,還是應該直接從控制器的方法處理?

回答

30

AuthorizationContext(OnAuthorize的參數)提供對Controller,RouteData,HttpContext等的訪問。您應該能夠在自定義授權過濾器中使用這些過濾器來執行您想要的操作。以下是從AuthorizeAttribute派生的RoleOrOwnerAttribute的代碼示例。

public override void OnAuthorization(AuthorizationContext filterContext) 
{ 
    if (filterContext == null) 
    { 
     throw new ArgumentNullException("filterContext"); 
    } 

    if (AuthorizeCore(filterContext.HttpContext)) // checks roles/users 
    { 
     SetCachePolicy(filterContext); 
    } 
    else if (!filterContext.HttpContext.User.Identity.IsAuthenticated) 
    { 
     // auth failed, redirect to login page 
     filterContext.Result = new HttpUnauthorizedResult(); 
    } 
    // custom check for global role or ownership 
    else if (filterContext.HttpContext.User.IsInRole("SuperUser") || IsOwner(filterContext)) 
    { 
     SetCachePolicy(filterContext); 
    } 
    else 
    { 
     ViewDataDictionary viewData = new ViewDataDictionary(); 
     viewData.Add("Message", "You do not have sufficient privileges for this operation."); 
     filterContext.Result = new ViewResult { MasterName = this.MasterName, ViewName = this.ViewName, ViewData = viewData }; 
    } 

} 

// helper method to determine ownership, uses factory to get data context, 
// then check the specified route parameter (property on the attribute) 
// corresponds to the id of the current user in the database. 
private bool IsOwner(AuthorizationContext filterContext) 
{ 
    using (IAuditableDataContextWrapper dc = this.ContextFactory.GetDataContextWrapper()) 
    { 
     int id = -1; 
     if (filterContext.RouteData.Values.ContainsKey(this.RouteParameter)) 
     { 
      id = Convert.ToInt32(filterContext.RouteData.Values[this.RouteParameter]); 
     } 

     string userName = filterContext.HttpContext.User.Identity.Name; 

     return dc.Table<Participant>().Where(p => p.UserName == userName && p.ParticipantID == id).Any(); 
    } 
} 


protected void SetCachePolicy(AuthorizationContext filterContext) 
{ 
    // ** IMPORTANT ** 
    // Since we're performing authorization at the action level, the authorization code runs 
    // after the output caching module. In the worst case this could allow an authorized user 
    // to cause the page to be cached, then an unauthorized user would later be served the 
    // cached page. We work around this by telling proxies not to cache the sensitive page, 
    // then we hook our custom authorization code into the caching mechanism so that we have 
    // the final say on whether a page should be served from the cache. 
    HttpCachePolicyBase cachePolicy = filterContext.HttpContext.Response.Cache; 
    cachePolicy.SetProxyMaxAge(new TimeSpan(0)); 
    cachePolicy.AddValidationCallback(CacheValidateHandler, null /* data */); 
} 
1

我的回答並不好,因爲它殺死了單元測試,但我從System.Web.HttpContext.Current.Session中提取了值。整個項目中都有單身人士。通過在會話中保存當前用戶,您可以從任何地方獲得該用戶,包括公用事業類,如AuthorizeAttribute

雖然我很想看到單元測試的解決方案。

+0

會話被低估。 – MrBoJangles 2013-01-28 22:06:15

2

如果授權真的是動態的,我會在控制器中處理它。我有一個操作,我這樣做 - 你可以返回一個HttpUnauthorizedResult重定向到登錄頁面,或者你可以在你的視圖中顯示一個自定義錯誤。

當有人已經登錄時,我不會默認重定向到登錄頁面,但不是正確的角色。這對用戶來說非常混亂。

+0

你可以在一個屬性中做同樣的事情,這個屬性可以通過裝飾在其他需要它的方法上很容易地應用。 – tvanfosson 2009-06-09 17:39:55

+0

是的,但是您必須在控制器(通常)中查詢模型。如果你在屬性中這樣做,你必須兩次查詢模型,除非你有一個聰明的緩存機制 – chris166 2009-06-09 18:23:41