web-dev-qa-db-fra.com

Asp.net MVC4: Autoriser à la fois le contrôleur et l'action

Si j'ai l'attribut Authorize à la fois sur le contrôleur et sur l'action, lequel prendra effet? Ou les deux entreront-ils en vigueur?

69
frank

Tu as demandé:

Si j'ai l'attribut Authorize sur le contrôleur et l'action, lequel prendra effet? Tous les deux?

Pour répondre à cela simplement: les deux. L'effet est de AND les deux restrictions ensemble. Je vais expliquer pourquoi ci-dessous ...

Détails

Donc, il y a quelques raisons pour lesquelles vous pourriez demander cela.

  1. Vous voulez savoir comment appliquer une contrainte supplémentaire sur une action par rapport à une méthode. par exemple.
    • Au niveau du contrôleur, imposer aux utilisateurs le rôle "utilisateur"
    • Au niveau de l'action, imposer en outre aux utilisateurs le rôle "admin"
  2. Vous voulez remplacer la contrainte du contrôleur au niveau de l'action
  3. Vous souhaitez supprimer la contrainte de contrôleur au niveau action et rendre la méthode disponible pour les utilisateurs anonymes

Vous n’avez pas spécifié votre version de MVC, je vais donc supposer la dernière en date (MVC 4.5). Cependant, cela ne changera pas beaucoup la réponse même si vous utilisiez MVC 3.

[Anonymous] Remplace le contrôleur [Authorize] (Cas 3)

Cas 3. Je n'ai pas besoin de couvrir (l'utilisation de [AllowAnonymous]) Car il a déjà été répondu sur tout SO et sur tout le Web déjà. Autant dire que si vous spécifiez [AllowAnonymous] Pour une action, elle sera rendue publique même si le contrôleur a [Authorize] Dessus.

Vous pouvez également soumettre un site Web entier à une autorisation par à l'aide d'un filtre global , et utiliser AllowAnonymous sur les quelques actions ou contrôleurs que vous souhaitez rendre publics.

[Authorize] Est additif (cas 1)

Le cas 1 est facile. Prenons l'exemple du contrôleur suivant:

[Authorize(Roles="user")]
public class HomeController : Controller {
    public ActionResult AllUsersIndex() {
        return View();
    }

    [Authorize(Roles = "admin")]
    public ActionResult AdminUsersIndex() {
        return View();
    }
}

Par défaut, [Authorize(Roles="user")] rend toutes les actions du contrôleur disponibles pour les comptes du rôle "utilisateur" uniquement. Par conséquent, pour accéder à AllUsersIndex, vous devez être dans le rôle "utilisateur". Cependant, pour accéder à AdminUsersIndex, vous devez être à la fois dans les rôles "utilisateur" et "admin". Par exemple:

  • UserName: Bob, Roles: user, ne peut pas accéder à AdminUsersIndex, mais peut accéder à AllUsersIndex
  • UserName: Jane, Rôles: admin, ne peut pas accéder à AdminUsersIndex ou AllUsersIndex
  • UserName: Tim, Roles: utilisateur et administrateur, peut accéder à AdminUsersIndex et AllUsersIndex.

Cela montre que l'attribut [Authorize] Est additif. Ceci est également vrai de la propriété Users de l'attribut, qui peut être combinée avec Roles pour la rendre encore plus restrictive.

Ce comportement est dû à la manière dont les attributs de contrôleur et d'action fonctionnent. Les attributs sont chaînés et appliqués dans le contrôleur d’ordre, puis à l’action. Si le premier refuse l'autorisation, le contrôle retourne et l'attribut de l'action n'est pas appelé. Si le premier passe l'autorisation, alors le second est également vérifié. Vous pouvez remplacer cet ordre en spécifiant Order (par exemple [Authorize(Roles = "user", Order = 2)]).

Remplacer [Authorize] (Cas 2)

Le cas 2 est plus compliqué. Rappelons ci-dessus que les attributs [Authorize] Sont examinés dans l'ordre (global puis) ​​contrôleur puis action. Le premier à détecter que l'utilisateur n'est pas autorisé à être autorisé gagne, les autres ne sont pas appelés.

Une solution consiste à définir deux nouveaux attributs, comme indiqué ci-dessous. Le [OverrideAuthorize] Ne fait que reporter à [Authorize]; son seul but est de définir un type que nous pouvons vérifier. Le [DefaultAuthorize] Nous permet de vérifier si l'action appelée dans la demande est décorée avec un [OverrideAuthorize]. Si c'est le cas, nous passons au contrôle d'autorisation des actions, sinon nous procédons au contrôle au niveau du contrôleur.

public class DefaultAuthorizeAttribute : AuthorizeAttribute {
    public override void OnAuthorization(AuthorizationContext filterContext)
    {
        var action = filterContext.ActionDescriptor;
        if (action.IsDefined(typeof(OverrideAuthorizeAttribute), true)) return;

        base.OnAuthorization(filterContext);
    }
}
public class OverrideAuthorizeAttribute : AuthorizeAttribute {
    public override void OnAuthorization(AuthorizationContext filterContext)
    {
        base.OnAuthorization(filterContext);
    }
}

Nous pouvons ensuite l'utiliser comme ceci:

[DefaultAuthorize(Roles="user")]
public class HomeController : Controller {
    // Available to accounts in the "user" role
    public ActionResult AllUsersIndex() {
        return View();
    }
    // Available only to accounts both in the "user" and "admin" role
    [Authorize(Roles = "admin")]
    public ActionResult AdminUsersIndex() {
        return View();
    }
    // Available to accounts in the "superuser" role even if not in "user" role
    [OverrideAuthorize(Roles = "superuser")]
    public ActionResult SuperusersIndex() {
        return View();
    }
}

Dans l'exemple ci-dessus, SuperusersIndex est disponible pour un compte doté du rôle "superutilisateur", même s'il ne possède pas le rôle "utilisateur".

147
Andy Brown

J'aimerais ajouter quelque chose à Remplacer [Autoriser] (cas 2)

OverrideAuthorizeAttribute et DefaultAuthorizeAttribute fonctionnent bien, mais je découvre que vous pouvez également utiliser OverrideAuthorizationAttribute qui remplace les filtres d'autorisation définis à un niveau supérieur.

[Authorize(Roles="user")]
public class HomeController : Controller {
    // Available to accounts in the "user" role
    public ActionResult AllUsersIndex() {
        return View();
    }
    // Available only to accounts both in the "user" and "admin" role
    [Authorize(Roles = "admin")]
    public ActionResult AdminUsersIndex() {
        return View();
    }
    // Available to accounts in the "superuser" role even if not in "user" role
    [OverrideAuthorization()]
    [Authorize(Roles = "superuser")]
    public ActionResult SuperusersIndex() {
        return View();
    }
}
43
Akodo_Shado

J'ai fait une adaptation de deuxième cas de cette réponse pour ASP.NET Core 2.1.

La différence avec AuthorizeAttribute d'ASP.NET Core est qu'il n'est pas nécessaire d'appeler AuthorizeAttribute.OnAuthorization méthode de base pour passer à l’autorisation normale. Cela signifie que même si vous n'appelez pas explicitement la méthode de base, la base AuthorizeAttribute pourrait toujours court-circuiter l'autorisation en interdisant l'accès.

Ce que j'ai fait, c'est que j'ai créé un DefaultAuthorizeAttribute qui n'hérite pas de AuthorizeAttribute, mais de Attribute à la place. Puisque DefaultAuthorizeAttribute n'hérite pas de AuthorizeAttribute, j'ai dû recréer le comportement d'autorisation.

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true)]
public class DefaultAuthorizeAttribute : Attribute, IAuthorizationFilter
{
    private readonly AuthorizeFilter m_authorizeFilter;

    public DefaultAuthorizeAttribute(params string[] authenticationSchemes)
    {
        var policyBuilder = new AuthorizationPolicyBuilder()
            .AddAuthenticationSchemes(authenticationSchemes)
            .RequireAuthenticatedUser();
        m_authorizeFilter = new AuthorizeFilter(policyBuilder.Build());
    }

    public void OnAuthorization(AuthorizationFilterContext filterContext)
    {
        if (filterContext.ActionDescriptor is ControllerActionDescriptor controllerAction
            && controllerAction.MethodInfo.GetCustomAttributes(typeof(OverrideAuthorizeAttribute), true).Any())
        {
            return;
        }
        m_authorizeFilter.OnAuthorizationAsync(filterContext).Wait();
    }
}

public class OverrideAuthorizeAttribute : AuthorizeAttribute, IAuthorizationFilter
{
    public void OnAuthorization(AuthorizationFilterContext filterContext) { }
}
2
Jo Ham

Si vous l'utilisez sur le contrôleur, toutes les méthodes de ce contrôleur seront appliquées.

[Authorize]
public class SomeController(){

    // all actions are effected
    public ActionResult Action1
    public ActionResult Action2

Si vous souhaitez empêcher l'une de ces actions, vous pouvez utiliser quelque chose comme ceci:

[Authorize]
public class SomeController(){

    // all actions are effected
    public ActionResult Action1
    public ActionResult Action2

    [AllowAnonymous]
    public ActionResult Action3 // only this method is not effected...
0