Autoryzuj atrybut z wieloma rolami

100

Chciałbym dodać uprawnienia do kontrolera dla wielu ról jednocześnie.

Zwykle wyglądałoby to tak:

[Authorize(Roles = "RoleA,RoleB,RoleC")]
public async Task<ActionResult> Index()
{
}

Ale zapisałem swoje role w stałych, ponieważ w pewnym momencie mogą się zmienić lub rozszerzyć.

public const RoleA = "RoleA";
public const RoleB = "RoleB";
public const RoleC = "RoleC";

Nie mogę tego zrobić, ponieważ ciąg znaków musi być znany w czasie kompilacji:

[Authorize(Roles = string.join(",",RoleA,RoleB,RoleC)]
public async Task<ActionResult> Index()
{
}

Czy istnieje sposób na obejście problemu?

Mógłbym napisać stałą, która po prostu zawiera „RoleA, RoleB, RoleC” - ale nie lubię magicznych ciągów, a to jest magiczny ciąg. Zmiana nazwy roli i zapomnienie o zmianie połączonego ciągu byłoby katastrofą.

Używam MVC5. Tożsamość ASP.NET i rola są znane w czasie kompilacji.

Christian Sauer
źródło
czy używasz public const string RoleA = "RoleA"; czy jak napisałeś w pytaniu?
Mukesh Modhvadiya

Odpowiedzi:

194

Spróbuj utworzyć niestandardowy atrybut autoryzacji, taki jak ten .

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

Zakładając, że twoje role będą takie same dla wielu kontrolerów, utwórz klasę pomocniczą:

public static class Role
{
    public const string Administrator = "Administrator";
    public const string Assistant = "Assistant";
}

Następnie użyj tego w ten sposób:

public class MyController : Controller
{
    [AuthorizeRoles(Role.Administrator, Role.Assistant)]
    public ActionResult AdminOrAssistant()
    {                       
        return View();
    }
}
MacGyver
źródło
12
To pomysł godny Maca Gyvera;)
Christian Sauer
2
Bardzo fajne rozwiązanie :)
aup
1
Bardzo podoba mi się to rozwiązanie, zwłaszcza że mogę pozwolić, aby moja rola była wyliczeniem, a nie ciągiem. Jaka byłaby dobra przestrzeń nazw i lokalizacja w hierarchii projektu do umieszczania tego niestandardowego atrybutu autoryzacji?
Simon Shine
4
Nie jestem pewien, co się tutaj dzieje, ale to NIE pomogło, każdy użytkownik niezależnie od roli mógł uzyskać dostęp do metody.
Urielzen
2
Ten sam problem, co @Urielzen, ale został rozwiązany przez poniższą odpowiedź Jerry'ego Finegana (przy użyciu „System.Web.Mvc.AuthorizeAttribute i NOT System.Web.Http.AuthorizeAttribute”)
RJB
13

Upewnij się, że wyprowadzasz niestandardową klasę atrybutów System.Web.Mvc.AuthorizeAttributei NIE System.Web.Http.AuthorizeAttribute.

Napotkałem ten sam problem. Kiedy to zmieniłem, wszystko działało.

Możesz również dodać następujące elementy do swojej klasy atrybutów niestandardowych:

[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = true, AllowMultiple = true)] 
Jerry Finegan
źródło
Właśnie wypróbowałem to i znalazłem odniesienie do biblioteki System.Web.Http.AuthorizeAttributeZAMIASTSystem.Web.Mvc.AuthorizeAttribute
fraser jordan
13

Najlepszym i najprostszym sposobem rozwiązania tego problemu jest po prostu połączenie ról w atrybucie Autoryzuj.

[Authorize(Roles = CustomRoles.Admin + "," + CustomRoles.OtherRole)]

z CustomRole klasa ze stałymi ciągami, takimi jak ta:

public static class CustomRoles
{
    public const string Admin = "Admin";
    // and so on..
}
ChristopheHvd
źródło
2
Wartościowe; ale to powinien być komentarz; nie jest odpowiedzią.
GhostCat
1
Proste i eleganckie rozwiązanie!
Iosif Bancioiu
Zarówno Twoja odpowiedź, jak i zaakceptowana odpowiedź spowodują autoryzację, jeśli zaimplementowana poprawnie (używam zaakceptowanej w produkcyjnej aplikacji internetowej). Zaproponowanie zmiany w celu usunięcia komentarzy na temat zaakceptowanej odpowiedzi.
Eric Eskildsen
Jeśli role były wyliczeniami, możesz użyć czegoś takiego jak [Authorize (Roles = nameof (UserRoleEnum.User) + "," + nameof (UserRoleEnum.Admin))]
Varun
3

To, co zrobiłem, to odpowiedź w @Tieson

Trochę poprawiam jego odpowiedź. Zamiast stringów, dlaczego by nie przekonwertować go na listę?

Oto moja odpowiedź:

public class AuthorizeRolesAttribute : AuthorizeAttribute
{
    private new List<string> Roles;
    public AuthorizeRolesAttribute(params string[] roles) : base()
    {
        Roles = roles.toList()
    }
}

Następnie sprawdź, czy rola jest prawidłowa, zastępując OnAuthorization

public override void OnAuthorization(HttpActionContext actionContext)
{
            if (Roles == null)
                HandleUnauthorizedRequest(actionContext);
            else
            {
                ClaimsIdentity claimsIdentity = HttpContext.Current.User.Identity as ClaimsIdentity;
                string _role = claimsIdentity.FindFirst(ClaimTypes.Role).Value;
                bool isAuthorize = Roles.Any(role => role == _role);

                if(!isAuthorize)
                    HandleUnauthorizedRequest(actionContext);
            }
        }

I masz to, teraz sprawdza, czy rola ma uprawnienia dostępu do zasobu

Christopher Enriquez
źródło
1

Wydaje mi się, że niestandardowy atrybut autoryzacji jest przesadą w przypadku tego problemu, chyba że masz dużą liczbę ról.

Ponieważ ciąg musi być znany w czasie kompilacji, dlaczego nie utworzyć statycznej klasy Role, która zawiera publiczne ciągi znaków zdefiniowanych ról, a następnie dodać ciągi oddzielone przecinkami z określonymi rolami, które chcesz autoryzować:

public static class Roles
{
    public const string ADMIN = "Admin";
    public const string VIEWER = "Viewer";

    public const string ADMIN_OR_VIEWER = ADMIN + "," + VIEWER;
}

Następnie możesz użyć atrybutu autoryzacji, tak jak w klasie kontrolera lub metodzie kontrolera (lub obu):

[Authorize(Roles = Roles.ADMIN]
public class ExampleController : Controller
{
    [Authorize(Roles = Roles.ADMIN_OR_VIEWER)
    public ActionResult Create()
    {
        ..code here...
    }
}
Robert Tuttle
źródło
1
Ten przykład nie działa, a przynajmniej nie tak, jak myślisz. Na przykład, podczas powieści, ADMIN_OR_VIEWERrola w akcji jest zbędna, ponieważ nie będziesz mógł dostać się do Createmetody, jeśli jeszcze jej nie masz ADMIN. W takim przypadku VIEWERnigdy nie będzie można wywołać Createmetody.
John Leidegren
To rozwiązanie również nie jest skalowalne.
Nadejdzie