2009-06-09 11 views
24

Ich habe eine Frage über benutzerdefinierte Autorisierung in MVC.ASP.NET MVC Benutzerdefinierte Autorisierung

Ich habe eine Website, die ich den Zugriff auf bestimmte Seiten einschränken möchte, abhängig von ihrer Gruppenmitgliedschaft. Nun habe ich unzählige Beispiele dafür gesehen, wenn es beispielsweise eine einzelne Admin-Gruppe und eine einzelne Benutzergruppe gibt, aber keine Beispiele für eine dritte Ebene.

Zum Beispiel können nur Benutzer eines Unternehmens Aufträge für ihr eigenes Unternehmen anzeigen (und jedes Unternehmen hat seine eigenen Administratoren usw.). Diese Firmen sind in einem DB gespeichert. So habe ich Möglichkeiten gesehen, benutzerdefinierte Autorisierung zu tun, überschreiben die AuthorizeCore Methode auf der AuthorizeAttribute, aber ich weiß nicht, wie man auf die Parameter in den Controller übergeben, um zu sehen, ob der Benutzer Zugriff auf die Bestellung hat (Bestell-ID, zum Beispiel)).

Ist dies der beste Ort für die Überprüfung, oder sollte dies direkt von der Methode des Controllers behandelt werden?

Antwort

30

Der AuthorizationContext (Parameter zu OnAuthorize) bietet Zugriff auf den Controller, RouteData, HttpContext usw. Sie sollten diese in einem benutzerdefinierten Autorisierungsfilter verwenden können, um das zu tun, was Sie wollen. Im Folgenden finden Sie ein Codebeispiel aus einem von AuthorizeAttribute abgeleiteten 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 */); 
} 
+0

Danke, das ist, was ich gesucht habe. Was ist die SetCachePolicy-Methode? – doobist

+0

Ich habe den Code von AuthorizeFilter überarbeitet, der die Caching-Richtlinie für die Seite festlegt, sodass nicht autorisierte Benutzer die Seite nicht aus dem Cache bedienen können. Ich füge dieses Snippet hinzu. – tvanfosson

+0

Das ist toll, danke tvanfosson! –

1

Meine Antwort ist nicht großartig, weil es Komponententests beendet, aber ich ziehe Werte von System.Web.HttpContext.Current.Session. Das Singleton ist während des gesamten Projekts verfügbar. Wenn Sie den aktuellen Benutzer in einer Sitzung speichern, können Sie ihn von überall abrufen, einschließlich Hilfsklassen wie AuthorizeAttribute.

Ich würde gerne eine Einheit-testbare Lösung sehen, obwohl.

+0

Sitzung ist unterbewertet. – MrBoJangles

2

Wenn die Autorisierung wirklich so dynamisch ist, würde ich es in der Steuerung behandeln. Ich habe eine Aktion, wo ich das tue - Sie können ein HttpUnauthorizedResult zurückleiten, um auf die Anmeldeseite umzuleiten, oder Sie können einen benutzerdefinierten Fehler in Ihrer Ansicht anzeigen.

Ich bin nicht die Standardumleitung auf die Anmeldeseite, wenn jemand bereits angemeldet ist, aber nicht in der richtigen Rolle. Das ist sehr verwirrend für den Benutzer.

+0

Sie könnten das gleiche in einem Attribut tun, das einfach über Dekoration auf andere Methoden angewendet werden kann, die es benötigen. – tvanfosson

+0

Ja, aber Sie müssen das Modell sowieso im Controller abfragen (normalerweise). Wenn Sie es in dem Attribut tun, müssen Sie das Modell zweimal abfragen, es sei denn, Sie haben einen cleveren Caching-Mechanismus – chris166

Verwandte Themen