Autoriser plusieurs rôles à accéder à l'action du contrôleur

274

En ce moment, je décore une méthode comme celle-ci pour permettre aux "membres" d'accéder à l'action de mon contrôleur

[Authorize(Roles="members")]

Comment autoriser plusieurs rôles? Par exemple, ce qui suit ne fonctionne pas, mais il montre ce que j'essaie de faire (autoriser l'accès "membres" et "admin"):

[Authorize(Roles="members", "admin")] 
codette
la source
4
Veuillez modifier la réponse acceptée à cette question. La personne avec la réponse actuellement acceptée l'a modifiée en indiquant qu'il avait tort.
Eric J.28

Réponses:

595

Une autre option consiste à utiliser un seul filtre d'autorisation lors de la publication, mais à supprimer les citations internes.

[Authorize(Roles="members,admin")]
Jim Schmehil
la source
5
Fonctionne également dans MVC 5. +1
gkonuralp
4
Fonctionne dans ASP.NET Core 1.0 (MVC 6) et Microsoft.AspNet.Identity v3. *
Soren
3
C'est correct si vous n'avez qu'un seul contrôleur que vous devez autoriser. Si vous en avez plusieurs, vous dupliquez ces constantes de chaîne (beurk). Je préfère de beaucoup la classe statique qui a les noms de rôle. Ma haine pour animaux de compagnie est des chaînes en double ... si mal.
robnick
1
@kraeg bonne nouvelle que vous avez résolu votre problème. Maintenant, pensez à supprimer vos commentaires, s'il vous plaît
Pablo Claus
1
Pourquoi? Cela m'a pris beaucoup de temps pour résoudre ce problème. Cela peut être utile pour quelqu'un d'autre qui rencontre le même problème.
kraeg
129

Si vous souhaitez utiliser des rôles personnalisés, vous pouvez le faire:

CustomRoles classe:

public static class CustomRoles
{
    public const string Administrator = "Administrador";
    public const string User = "Usuario";
}

Usage

[Authorize(Roles = CustomRoles.Administrator +","+ CustomRoles.User)]

Si vous avez peu de rôles, vous pouvez peut-être les combiner (pour plus de clarté) comme ceci:

public static class CustomRoles
{
     public const string Administrator = "Administrador";
     public const string User = "Usuario";
     public const string AdministratorOrUser = Administrator + "," + User;  
}

Usage

[Authorize(Roles = CustomRoles.AdministratorOrUser)]
Pablo Claus
la source
7
Ce serait une bonne réponse, si vous expliquiez aux personnes qui ne savaient pas ce qui se cache derrière les rôles personnalisés.
James Skemp
1
@JamesSkemp ok, j'ai étendu ma réponse. C'est très simple. CustumRoles est une classe que j'ai créée qui contient des constantes, qui correspond à mes rôles d'application. Je l'ai fait pour plusieurs raisons: 1) Il permet d'utiliser l'intellisense pour éviter les fautes d'orthographe 2) Pour simplifier la maintenance. Si un rôle change, je dois mettre à jour un seul endroit dans ma candidature.
Pablo Claus
@Pabloker Vous pouvez également créer une énumération avec un attribut Flags, par exemple Convert.ToString (CustomRoles.Administrator | CustomRoles.User); - une partie ennuyeuse est que cela nécessite une conversion explicite
cstruter
Si vous avez 39 rôles?
Kiquenet
Je pense que votre problème passe par la modélisation des permis au-delà de ce qui peut être fait avec .net
Pablo Claus
82

Une simplification possible serait de sous AuthorizeAttribute- classer :

public class RolesAttribute : AuthorizeAttribute
{
    public RolesAttribute(params string[] roles)
    {
        Roles = String.Join(",", roles);
    }
}

Usage:

[Roles("members", "admin")]

Sémantiquement, c'est la même chose que la réponse de Jim Schmehil.

Mihkel Müür
la source
3
Cela ne fonctionne pas pour moi, l'utilisateur connecté a pu contourner l'attribut même si l'utilisateur n'avait aucun des rôles.
Urielzen
9
Cette réponse est meilleure lorsque vous utilisez des constantes comme valeurs: ie [Rôles (Constants.Admin, Constants.Owner)]
dalcam
3
c'est la meilleure réponse
IgorShch
18

Pour MVC4, en utilisant un Enum( UserRoles) avec mes rôles, j'utilise une coutume AuthorizeAttribute.

Sur mon action contrôlée, je fais:

[CustomAuthorize(UserRoles.Admin, UserRoles.User)]
public ActionResult ChangePassword()
{
    return View();
}

Et j'utilise une coutume AuthorizeAttributecomme ça:

[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = true, AllowMultiple = true)]
public class CustomAuthorize : AuthorizeAttribute
{
    private string[] UserProfilesRequired { get; set; }

    public CustomAuthorize(params object[] userProfilesRequired)
    {
        if (userProfilesRequired.Any(p => p.GetType().BaseType != typeof(Enum)))
            throw new ArgumentException("userProfilesRequired");

        this.UserProfilesRequired = userProfilesRequired.Select(p => Enum.GetName(p.GetType(), p)).ToArray();
    }

    public override void OnAuthorization(AuthorizationContext context)
    {
        bool authorized = false;

        foreach (var role in this.UserProfilesRequired)
            if (HttpContext.Current.User.IsInRole(role))
            {
                authorized = true;
                break;
            }

        if (!authorized)
        {
            var url = new UrlHelper(context.RequestContext);
            var logonUrl = url.Action("Http", "Error", new { Id = 401, Area = "" });
            context.Result = new RedirectResult(logonUrl);

            return;
        }
    }
}

Cela fait partie du FNHMVC modifié par Fabricio Martínez Tamayo https://github.com/fabriciomrtnz/FNHMVC/

Bernardo Loureiro
la source
1
Votre méthode OnAuthorization exigera que l'utilisateur ait tous les rôles énumérés; était-ce intentionnel, ou vous manquez une pause dans cette boucle?
Tieson T.
@Tieson: J'ai inspecté cela de près, il semble définitivement qu'une pause serait nécessaire dans cette boucle.
OcelotXL
@TiesonT. et @ madrush, j'apprécie votre correctif, il pourrait vraiment avoir une pause à l'intérieur de la boucle. Je vais changer le code ci-dessus.
Bernardo Loureiro
L'énumération UserRoles est sympa. Le déclarez-vous manuellement ou est-il généré automatiquement en fonction du contenu de la base de données?
Konrad Viltersten
@KonradViltersten C'est manuellement mais je suppose qu'avec la réflexion et la classe dynamique peuvent être générées automatiquement
Bernardo Loureiro
3

Une autre solution claire, vous pouvez utiliser des constantes pour conserver la convention et ajouter plusieurs attributs [Autoriser]. Regarde ça:

public static class RolesConvention
{
    public const string Administrator = "Administrator";
    public const string Guest = "Guest";
}

Puis dans le contrôleur:

[Authorize(Roles = RolesConvention.Administrator )]
[Authorize(Roles = RolesConvention.Guest)]
[Produces("application/json")]
[Route("api/[controller]")]
public class MyController : Controller
Renê R. Silva
la source
14
Plusieurs Authorizeattributs utilisent la sémantique ET et nécessitent que TOUTES les conditions soient remplies (c'est-à-dire que l'utilisateur doit être à la fois dans les rôles Administrateur et Invité).
pantalon
3

Si vous vous retrouvez à appliquer ces 2 rôles souvent, vous pouvez les envelopper dans leur propre autorisation. C'est vraiment une extension de la réponse acceptée.

using System.Web.Mvc;

public class AuthorizeAdminOrMember : AuthorizeAttribute
{
    public AuthorizeAdminOrMember()
    {
        Roles = "members, admin";
    }
}

Et puis appliquez votre nouvelle autorisation à l'action. Je pense que cela semble plus propre et se lit facilement.

public class MyController : Controller
{
    [AuthorizeAdminOrMember]
    public ActionResult MyAction()
    {
        return null;
    }
}
GER
la source
1

Meilleur code avec l'ajout d'une sous-classe AuthorizeRole.cs

    [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = true, AllowMultiple = true)]
    class AuthorizeRoleAttribute : AuthorizeAttribute
    {
        public AuthorizeRoleAttribute(params Rolenames[] roles)
        {
            this.Roles = string.Join(",", roles.Select(r => Enum.GetName(r.GetType(), r)));
        }
        protected override void HandleUnauthorizedRequest(System.Web.Mvc.AuthorizationContext filterContext)
        {
            if (filterContext.HttpContext.Request.IsAuthenticated)
            {
                filterContext.Result = new RedirectToRouteResult(
                new RouteValueDictionary {
                  { "action", "Unauthorized" },
                  { "controller", "Home" },
                  { "area", "" }
                  }
              );
                //base.HandleUnauthorizedRequest(filterContext);
            }
            else
            {
                filterContext.Result = new RedirectToRouteResult(
                new RouteValueDictionary {
                  { "action", "Login" },
                  { "controller", "Account" },
                  { "area", "" },
                  { "returnUrl", HttpContext.Current.Request.Url }
                  }
              );
            }
        }
    }

Comment l'utiliser

[AuthorizeRole(Rolenames.Admin,Rolenames.Member)]

public ActionResult Index()
{
return View();
}
Goel kinzzy
la source
1

En utilisant AspNetCore 2.x, vous devez procéder différemment:

[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = true, AllowMultiple = true)]
public class AuthorizeRoleAttribute : AuthorizeAttribute
{
    public AuthorizeRoleAttribute(params YourEnum[] roles)
    {
        Policy = string.Join(",", roles.Select(r => r.GetDescription()));
    }
}

il suffit de l'utiliser comme ceci:

[Authorize(YourEnum.Role1, YourEnum.Role2)]
Daniel DirtyNative Martin
la source
-2
Intent promptInstall = new Intent(android.content.Intent.ACTION_VIEW);
promptInstall.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
promptInstall.setDataAndType(Uri.parse("http://10.0.2.2:8081/MyAPPStore/apk/Teflouki.apk"), "application/vnd.android.package-archive" );

startActivity(promptInstall);
Orsit Moel
la source
1
Les réponses, y compris le code, devraient avoir au moins une description minimale expliquant comment le code fonctionne et pourquoi il répond à la question. En outre, le formatage de la section de code doit être amélioré.
Roberto Caboni
Hein? @Orsit Moel, On dirait que copier-coller dans un mauvais fil ...
Cameron Forward