Zezwalaj wielu rolom na dostęp do akcji kontrolera

274

Obecnie dekoruję taką metodę, aby umożliwić „członkom” dostęp do mojej akcji kontrolera

[Authorize(Roles="members")]

Jak zezwolić na więcej niż jedną rolę? Na przykład poniższe polecenie nie działa, ale pokazuje, co próbuję zrobić (zezwól na dostęp dla „członków” i „administratora”):

[Authorize(Roles="members", "admin")] 
kodeta
źródło
4
Zmień zaakceptowaną odpowiedź na to pytanie. Osoba z aktualnie zaakceptowaną odpowiedzią zredagowała ją, wskazując, że się mylił.
Eric J.

Odpowiedzi:

595

Inną opcją jest użycie pojedynczego filtra autoryzacji podczas publikowania, ale usunięcie wewnętrznych cytatów.

[Authorize(Roles="members,admin")]
Jim Schmehil
źródło
5
Działa również w MVC 5. +1
gkonuralp
4
Działa w ASP.NET Core 1.0 (MVC 6) i Microsoft.AspNet.Identity v3. *
Soren
3
Jest to w porządku, jeśli masz tylko jeden kontroler, który musisz autoryzować. Jeśli masz więcej niż jeden, duplikujesz te stałe ciągów (fuj). Wolę klasę statyczną, która ma nazwy ról. Moją nienawiścią dla zwierząt domowych są zdublowane struny ... takie straszne.
robnick
1
@kraeg dobre wieści, że rozwiązałeś problem. Zastanów się teraz nad usunięciem swoich komentarzy
Pablo Claus,
1
Czemu? Zajęło mi to wieki, aby to wypracować. Może to być pomocne dla kogoś, kto ma ten sam problem.
kraeg
129

Jeśli chcesz użyć niestandardowych ról, możesz to zrobić:

CustomRoles klasa:

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

Stosowanie

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

Jeśli masz kilka ról, być może możesz je połączyć (dla jasności) w następujący sposób:

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

Stosowanie

[Authorize(Roles = CustomRoles.AdministratorOrUser)]
Pablo Claus
źródło
7
To byłaby dobra odpowiedź, gdybyś wyjaśnił ludziom, którzy nie wiedzieli, co kryje się za CustomRoles.
James Skemp
1
@JamesSkemp ok, rozszerzyłem swoją odpowiedź. To bardzo proste. CustumRoles to utworzona przeze mnie klasa, która zawiera pewne stałe, które odpowiadają moim rolom aplikacji. Zrobiłem to z kilku powodów: 1) Pozwala to na użycie inteligencji, aby uniknąć błędów ortograficznych 2) W celu uproszczenia konserwacji. W przypadku zmiany roli muszę zaktualizować tylko jedno miejsce w mojej aplikacji.
Pablo Claus
@Pabloker Alternatywnie możesz utworzyć wyliczenie z atrybutem Flagi, np. Convert.ToString (CustomRoles.Administrator | CustomRoles.User); - denerwujące jest to, że wymaga to wyraźnej konwersji
cstruter
Jeśli masz 39 ról?
Kiquenet
Myślę, że twój problem dotyczy modelowania pozwoleń wykraczającego poza to, co można zrobić z .net
Pablo Claus
82

Jednym z możliwych uproszczeń byłoby podklasa AuthorizeAttribute:

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

Stosowanie:

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

Semantycznie jest to to samo, co odpowiedź Jima Schmehila.

Mihkel Müür
źródło
3
To nie działało dla mnie, zalogowany użytkownik mógł ominąć atrybut, nawet jeśli użytkownik nie miał żadnej roli.
Urielzen
9
Ta odpowiedź jest lepsza, gdy używasz stałych jako wartości: tj. [Role (Constants.Admin, Constants.Owner)]
dalcam
3
to najlepsza odpowiedź
IgorShch
18

W przypadku MVC4, używając Enum( UserRoles) z moimi rolami, używam niestandardowego AuthorizeAttribute.

Po moim kontrolowanym działaniu robię:

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

I używam takiego zwyczaju AuthorizeAttribute:

[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;
        }
    }
}

Jest to część zmodyfikowanego FNHMVC autorstwa Fabricio Martínez Tamayo https://github.com/fabriciomrtnz/FNHMVC/

Bernardo Loureiro
źródło
1
Twoja metoda OnAuthorization będzie wymagała od użytkownika wszystkich wyliczonych ról; czy było to celowe, czy brakuje ci przerwy w tej pętli?
Tieson T.
@Tieson: Sprawdziłem to dość dokładnie, na pewno wygląda na to, że przerwa w tej pętli byłaby wymagana.
OcelotXL
@TiesonT. i @ madrush, doceniam twoją poprawkę, to naprawdę może mieć przerwę w pętli. Zmienię powyższy kod.
Bernardo Loureiro,
Wyliczenia UserRoles jest miły. Czy deklarujesz to ręcznie, czy jest generowany automatycznie na podstawie zawartości bazy danych?
Konrad Viltersten
@KonradViltersten Jest to ręczne, ale myślę, że można generować autogenerację klasy Reflection i Dynamic
Bernardo Loureiro
3

Kolejne jasne rozwiązanie, możesz użyć stałych, aby zachować konwencję i dodać wiele atrybutów [Autoryzuj]. Sprawdź to:

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

Następnie w kontrolerze:

[Authorize(Roles = RolesConvention.Administrator )]
[Authorize(Roles = RolesConvention.Guest)]
[Produces("application/json")]
[Route("api/[controller]")]
public class MyController : Controller
Renê R. Silva
źródło
14
Wiele Authorizeatrybutów wykorzystuje semantykę AND i wymaga spełnienia WSZYSTKICH warunków (tj. Użytkownik musi pełnić role administratora i gościa).
trousyt
3

Jeśli często stosujesz te 2 role, możesz zawinąć je we własną autoryzację. To naprawdę rozszerzenie przyjętej odpowiedzi.

using System.Web.Mvc;

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

A następnie zastosuj nową autoryzację do akcji. Myślę, że to wygląda na czystsze i czyta się łatwiej.

public class MyController : Controller
{
    [AuthorizeAdminOrMember]
    public ActionResult MyAction()
    {
        return null;
    }
}
GER
źródło
1

Lepszy kod dzięki dodaniu podklasy 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 }
                  }
              );
            }
        }
    }

Jak tego użyć

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

public ActionResult Index()
{
return View();
}
kinzzy goel
źródło
1

Korzystając z AspNetCore 2.x, musisz przejść nieco inną drogę:

[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()));
    }
}

użyj tego w następujący sposób:

[Authorize(YourEnum.Role1, YourEnum.Role2)]
Daniel DirtyNative Martin
źródło
-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
źródło
1
Odpowiedzi zawierające kod powinny zawierać przynajmniej minimalny opis wyjaśniający, jak działa kod i dlaczego odpowiada na pytanie. Ponadto należy poprawić formatowanie sekcji kodu.
Roberto Caboni
Co? @Orsit Moel, Wygląda na wklejony do niewłaściwego wątku ...
Cameron Forward