web-dev-qa-db-fra.com

Comment définir l'autorisation pour une zone entière dans ASP.NET MVC?

J'ai une zone d'administration et je veux que seuls les administrateurs y entrent. J'ai envisagé d'ajouter l'attribut autorisé à chaque contrôleur de la zone d'administration. N'y a-t-il pas une solution élégante ou cette fonctionnalité n'est-elle pas présente dans le framework lui-même?

EDIT: Je suis désolé, j'aurais dû le mentionner avant. J'utilise un AuthorizedAttribute personnalisé dérivé de AuthorizeAttribute.

53
Abdulsattar Mohammed

La sécurité basée sur Web.config devrait presque jamais être utilisée dans une application MVC. La raison en est que plusieurs URL peuvent potentiellement frapper un contrôleur, et mettre ces vérifications dans Web.config manque toujours quelque chose. N'oubliez pas - les contrôleurs ne sont pas associés à des zones, les itinéraires sont associés à des zones. L'usine de contrôleurs MVC servira avec plaisir les contrôleurs du dossier Zones/pour les demandes non liées aux zones s'il n'y a pas de conflit.

Par exemple, en utilisant la structure de projet par défaut, en ajoutant une zone d'administration avec un AdminDefaultController, vous pouvez accéder à ce contrôleur via/Admin/AdminDefault/Index et /AdminDefault /Indice.

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

53
Levi

Je viens d'enquêter sur ce même problème. Puisqu'il est pas 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 la sécurité requise à cela. Il vous suffit ensuite de vous assurer que chaque contrôleur de la zone remplace AreaController au lieu de Controller. Par exemple:

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

Il nécessite toujours que vous dériviez chaque contrôleur de la zone Admin 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.

45
Quango

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;
    }
}
14
Brian Rice

Si tout votre code administrateur est dans un seul contrôleur, ajoutez Authorize à toute la classe.

[Authorize]
public class AdminController : Controller
{
     .......
}
13
John

La réponse actuellement acceptée n'est pas la solution la plus sûre car elle nécessite que le développeur toujours se souvienne d'hériter de cette nouvelle classe de base pour tout nouveau contrôleur ou action ("blacklisting"; permettant aux utilisateurs d'accéder à tout sauf si une action est limité manuellement). Cela pose surtout des problèmes lorsque de nouveaux développeurs, qui ne connaissent pas vos rituels, sont introduits dans le projet. Il est facile d'oublier d'hériter de la classe de contrôleur appropriée si cela est fait de cette façon, surtout après avoir quitté le projet 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écurisée à ce problème consiste à refuser l'accès aux demandes toutes, puis à décorer chaque action avec les rôles qui sont autorisés à accéder aux actions ("liste blanche"; empêcher l'accès à tous les utilisateurs sauf autorisation manuelle) . Maintenant, si un développeur oublie de mettre sur liste blanche l'autorisation appropriée, les utilisateurs vous le feront savoir et c'est aussi simple que de regarder d'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 ne soit connecté en tant qu'administrateur. Ensuite, pour chaque action à laquelle vous souhaitez qu'un autre utilisateur autorisé ait accès, il vous suffit de le décorer avec [OverrideAuthorization] Et [Authorize].

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

Exemple 1 - Seuls les utilisateurs administrateurs et répartiteurs connectés seront autorisés à accéder aux méthodes Index() Get et Post.

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 Index() du contrôleur domestique.

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

}

Exemple - Les utilisateurs non authentifiés (c'est-à-dire les utilisateurs anonymes) peuvent être autorisés à accéder aux méthodes 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 auront accès aux méthodes qui n'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.) en raison du filtre global. Tout utilisateur non autorisé sera redirigé vers l'écran de connexion.

L'utilisation de l'attribut [OverrideAuthorization] Fait que l'action ignore le filtre global que vous définissez. Par conséquent, vous devez réappliquez l'attribut [Authorize] Chaque fois que vous utilisez la substitution afin que l'action reste sécurisée.

Concernant des zones entières et des contrôleurs

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

5
aiwyn