2010-02-23 13 views
52

J'ai une zone d'administration et je veux que seuls les administrateurs entrent dans la zone. J'ai envisagé d'ajouter l'attribut Autorisé à chaque contrôleur dans la zone Admin. N'y a-t-il pas une solution élégante ou cette fonctionnalité n'est-elle pas présente dans le cadre lui-même?Comment pouvons-nous définir l'autorisation pour une zone entière dans ASP.NET MVC?

EDIT: Je suis désolé, je devrais avoir mentionné cela avant. J'utilise un AuthorizedAttribute personnalisé dérivé de AuthorizeAttribute.

+0

Voir mon blog [Sécurisation de votre application ASP.NET MVC 3] (http://blogs.msdn.com/b/rickandy/archive/2011/05/02/securing-your-asp-net-mvc- 3-Application.aspx) – RickAndMSFT

+0

Voir mon blog Sécurisation de votre ASP.NET MVC 4 App et le nouveau AllowAnonymous attribut – RickAndMSFT

+0

Link pour le dernier commentaire de Rick -> http://blogs.msdn.com/b/rickandy/archive/2012/ 03/23/sécurisation-votre-asp-net-mvc-4-app-et-la-nouvelle-AllowAnonymous-attribute.aspx –

Répondre

51

sécurité basée web.config devrait presque jamais être utilisé dans une application MVC. La raison en est que plusieurs URL peuvent potentiellement atteindre un contrôleur, et mettre ces vérifications dans Web.config manque invariablement quelque chose. Rappelez-vous que les contrôleurs ne sont pas associés aux zones, les itinéraires sont associés aux zones. La fabrique de contrôleurs MVC servira volontiers les contrôleurs depuis le dossier Zones/pour les demandes hors zone s'il n'y a pas de conflit. Par exemple, en utilisant la structure de projet par défaut, en ajoutant une zone Admin avec un AdminDefaultController, vous pouvez frapper ce contrôleur via/Admin/AdminDefault/Index et/AdminDefault/Index.

La seule solution prise en charge consiste à placer votre attribut sur une classe de base de contrôleur et à garantir que chaque contrôleur de la zone sous-classe cette classe de base.

+0

Eh bien, est-ce un bon moyen de s'assurer qu'un contrôleur est touché uniquement via une seule URL? Par un rabotage correct des routes peut-être? –

+1

Il n'y a aucun moyen de s'assurer qu'un contrôleur est accessible uniquement via une seule URL. Les routes sont simplement un mécanisme d'accès aux contrôleurs; ils ne sont pas * le * mécanisme. C'est pourquoi tous les attributs de sécurité doivent être appliqués directement aux contrôleurs eux-mêmes, et non aux routes (et par extension, aux zones). Par exemple, considérez l'introduction de MvcHandler.ashx dans MVC 3. Cela ferait appel directement au framework MVC en ignorant * all * de Routing. – Levi

+0

Ok ... cela signifie-t-il qu'il n'y a pas de réponse correcte à la question à côté d'une utilisation correcte des attributs de sécurité? –

-5

.. très crûment je crois que vous voulez quelque chose comme ça?

Quick and dirty role management

[Authorize(Roles = "Admins")] 
public ActionResult Register() 
{ 
    ViewData["roleName"] = new SelectList(Roles.GetAllRoles(), "roleName"); 
    ViewData["PasswordLength"] = MembershipService.MinPasswordLength; 
    return View(); 
} 
+4

s'il vous plaît lire une question ... – icesar

13

Si tous votre code d'administration est dans un contrôleur puis ajouter à la classe Autorisez entier.

[Authorize] 
public class AdminController : Controller 
{ 
    ....... 
} 
+2

qui fonctionne bien pour un seul contrôleur. Mais comment le faisons-nous pour toute une zone? – ppumkin

42

Je viens d'enquêter sur ce même problème. Puisqu'il est et non possible de sécuriser les contrôleurs en fonction des zones, une option plus simple vient à l'esprit.

Créez une définition de contrôleur de base pour chaque zone qui remplace le contrôleur, et ajoutez les exigences de sécurité à cela. Ensuite, vous devez simplement vous assurer que chaque contrôleur de la zone remplace OverController au lieu de Controller. Par exemple:

/// <summary> 
/// Base controller for all Admin area 
/// </summary> 
[Authorize(Roles = "Admin")] 
public abstract class AdminController : Controller { } 

Il ne nécessite toujours que vous dérivez chaque contrôleur dans la zone d'administration de cette base,

public class HomeController : AdminController 
{ 
    // .. actions 
} 

mais au moins vous avez un seul point où vous définissez la sécurité de la zone .

+2

Oui, cela semble être une bonne idée. En outre MSDN suggère des solutions similaires pour d'autres problèmes. L'héritage est bon. J'aime cette réponse. – ppumkin

+1

Je ne comprends vraiment pas pourquoi vous devez sous-classer chaque contrôleur dans la section admin au lieu d'écrire simplement votre attribut de 1 ligne au-dessus de la définition de classe. – Gudradain

+4

La réponse est simple DRY - http://en.wikipedia.org/wiki/Don't_repeat_yourself - je peux changer les rôles sont protégés dans une ligne de code, au lieu de la chasse pour chaque attribut [Authorize] – Quango

13

Je viens juste de commencer ... mais jusqu'à présent cela fonctionne plutôt bien pour moi.

Je crée une classe AuthorizeAttribute personnalisée et l'ajoute dans la fonction RegisterGlobalFilters.

Dans CustomAuthorizeAttribute, je vérifie diverses conditions en fonction de la zone dans laquelle il se trouve.

public class FilterConfig 
{ 
    public static void RegisterGlobalFilters(GlobalFilterCollection filters) 
    { 
     filters.Add(new CustomAuthorizeAttribute()); 
     filters.Add(new HandleErrorAttribute()); 
    } 
} 

public class CustomAuthorizeAttribute : AuthorizeAttribute 
{ 
    protected override bool AuthorizeCore(HttpContextBase httpContext) 
    { 
     var routeData = httpContext.Request.RequestContext.RouteData; 
     var controller = routeData.GetRequiredString("controller"); 
     var action = routeData.GetRequiredString("action"); 
     var area = routeData.DataTokens["area"]; 
     var user = httpContext.User; 
     if (area != null && area.ToString() == "Customer") 
     { 
      if (!user.Identity.IsAuthenticated) 
       return false; 
     } 
     else if (area != null && area.ToString() == "Admin") 
     { 
      if (!user.Identity.IsAuthenticated) 
       return false; 
      if (!user.IsInRole("Admin")) 
       return false; 
     } 
     return true; 
    } 
} 
+0

merci. C'est un bon moyen et un avantage. –

0

La réponse actuellement acceptée est pas la solution la plus sûre car elle nécessite le développeur de toujours se rappeler d'hériter de cette nouvelle classe de base pour tous les nouveaux contrôleurs ou des actions (« listes noires », qui permet aux utilisateurs d'accéder à tout, sauf une action est restreinte manuellement). Cela pose surtout des problèmes lorsque de nouveaux développeurs, ne connaissant pas vos rituels, sont introduits dans le projet. Il est facile d'oublier d'hériter de la bonne classe de contrôleurs si c'est fait de cette façon, surtout après avoir perdu de vue le projet pendant des semaines, des mois ou des années. Si un développeur oublie d'hériter, il n'est pas évident qu'il existe une vulnérabilité de sécurité dans le projet.

Une solution plus sûre à ce problème est de refuser l'accès à toutes demandes, puis décorer chaque action avec les rôles qui sont autorisés à accéder aux actions (« liste blanche », empêchant l'accès à tous les utilisateurs, sauf autorisation manuellement). Maintenant, si un développeur oublie de mettre en liste blanche l'autorisation appropriée, les utilisateurs vous le feront savoir et il est aussi simple que de regarder les autres contrôleurs pour un rappel sur la façon de donner un accès approprié. Cependant, au moins, il n'y a pas de vulnérabilité de sécurité majeure.

Dans le fichier App_Start/FilterConfig.cs, modifiez la classe FilterConfig:

public static void RegisterGlobalFilters(GlobalFilterCollection filters) 
    { 
     ... 

     //Deny access to all controllers and actions so that only logged in Administrators can access them by default 
     filters.Add(new System.Web.Mvc.AuthorizeAttribute() { Roles = "Administrator" }); 
    } 

Cela rend toutes les actions inaccessibles à moins que l'utilisateur est connecté en tant qu'administrateur. Ensuite, pour chaque action à laquelle vous souhaitez qu'un utilisateur autorisé différent ait accès, vous devez simplement le décorer avec [OverrideAuthorization] et [Authorize].

Dans votre logique métier, vous pouvez utiliser l'attribut Autoriser de différentes manières sans avoir à vous soucier de l'accès à des fonctionnalités par des utilisateurs non autorisés. Voici quelques exemples.

Exemple 1 - Seuls les utilisateurs Administrateur et Dispatcher connectés pourront accéder aux méthodes Get et Post Index().

public class MarkupCalculatorController : Controller //Just continue using the default Controller class. 
{ 
    // GET: MarkupCalculator 
    [OverrideAuthorization] 
    [Authorize(Roles = "Administrator,Dispatcher")] 
    public ActionResult Index() 
    { 
     //Business logic here. 

     return View(...); 
    } 

    // POST: DeliveryFeeCalculator 
    [HttpPost] 
    [ValidateAntiForgeryToken] 
    [OverrideAuthorization] 
    [Authorize(Roles = "Administrator,Dispatcher")] 
    public ActionResult Index([Bind(Include = "Price,MarkedupPrice")] MarkupCalculatorVM markupCalculatorVM) 
    { 
     //Business logic here. 

     return View(...); 
    } 
} 

Exemple 2 - Seuls les utilisateurs authentifiés seront autorisés à accéder à la méthode de contrôleur Home Index().

public class HomeController : Controller 
{ 
    [OverrideAuthorization] 
    [Authorize] //Allow all authorized (logged in) users to use this action 
    public ActionResult Index() 
    { 
     return View(); 
    } 

} 

Exemple 3 - Les utilisateurs non authentifiés (à savoir des utilisateurs anonymes) peuvent être autorisés à des méthodes d'accès en utilisant l'attribut [AllowAnonymous]. Cela remplace également automatiquement le filtre global sans avoir besoin de l'attribut [OverrideAuthorization].

// GET: /Account/Login 
    [AllowAnonymous] 
    public ActionResult Login(string returnUrl) 
    { 
     ViewBag.ReturnUrl = returnUrl; 
     return View(); 
    } 

    // 
    // POST: /Account/Login 
    [HttpPost] 
    [AllowAnonymous] 
    [ValidateAntiForgeryToken] 
    public async Task<ActionResult> Login(LoginViewModel model, string returnUrl) 
    { 
     ... 
    } 

Exemple 4 - Seuls les administrateurs seront autorisés à accéder à des méthodes qui ne ont pas l'attribut [Authorize].

public class LocationsController : Controller 
{ 

    // GET: Locations 
    public ActionResult Index() 
    { 
     //Business logic here. 
     return View(...); 
    } 
} 

Quelques notes.

Vous devez utiliser l'attribut [OverrideAuthorization] si vous souhaitez limiter l'accès à une action particulière à des rôles spécifiques. Sinon, les propriétés de l'attribut [Authorize] seront ignorées et seul le rôle par défaut (Administrateur dans mon exemple) sera autorisé, même si vous spécifiez d'autres rôles (par exemple, Dispatcher, etc.) à cause du filtre global. Tout utilisateur non autorisé sera redirigé vers l'écran de connexion.

En utilisant l'attribut [OverrideAuthorization] provoque l'action d'ignorer le filtre global défini. Par conséquent, doit réappliquer l'attribut [Authorize] chaque fois que vous utilisez le remplacement afin que l'action reste sécurisée.

En ce qui concerne les zones entières et les contrôleurs

Pour restreindre par zones, comme vous le demandez, mettre les attributs [OverrideAuthorization] et [Authorize] sur le contrôleur au lieu des actions individuelles.