2014-06-12 12 views
66

Chciałbym dodać autoryzację do kontrolera, dla wielu ról naraz.Autoryzuj atrybut z wieloma rolami

Normalnie, że będzie wyglądać następująco:

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

Ale mam przechowywać swoje role w consts, ponieważ mogą one zmienić lub zostać przedłużony w pewnym momencie.

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

Nie mogę tego zrobić, ponieważ łańcuch musi być znany w czasie kompilacji:

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

czy jest jakiś sposób na obejście tego problemu?

Mogę napisać const, który po prostu zawiera "RoleA, RoleB, RoleC" - ale nie lubię magicznych ciągów znaków i jest to magiczny ciąg znaków. Zmiana nazwy roli i zapomnienie zmiany połączonego ciągu byłoby katastrofą.

Używam MVC5. Identyfikacja ASP.NET i rola są znane w czasie kompilacji.

+0

czy używasz publicznych ciągów stałych RoleA = "RoleA"; lub jak pisałeś? –

+1

możliwy duplikat [zezwól na dostęp wielu kontrolerom do ról kontrolera] (http://stackoverflow.com/questions/700166/allow-multiple-roles-to-access-controller-action) –

Odpowiedz

121

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

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

Zakładając swoje role będą takie same dla wielu kontrolerów, utworzyć klasy pomocnika:

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

następnie używać go tak:

public class MyController : Controller 
{ 
    [AuthorizeRoles(Role.Administrator, Role.Assistant)] 
    public ActionResult AdminOrAssistant() 
    {      
     return View(); 
    } 
} 
+6

To jest pomysł godny Mac Gyvera;) –

+1

Bardzo fajne rozwiązanie :) – aup

+0

Bardzo podoba mi się również to rozwiązanie, szczególnie dlatego, że mogę pozwolić mojej Role być raczej enum niż stringiem. Jaka byłaby dobra przestrzeń nazw i lokalizacja w hierarchii projektu do umieszczenia tego niestandardowego atrybutu autoryzacji? –

6

Upewnij się, że w coraz większym stopniu swój zwyczaj klasa atrybutu wyłączona System.Web.Mvc.AuthorizeAttribute i NOT System.Web.Http.AuthorizeAttribute.

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

Można również dodać następujące do niestandardowej klasy atrybutu:

[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = true, AllowMultiple = true)] 
3

Nie wiem dlaczego wszyscy przyjął tę odpowiedź. Zrobiłem dokładnie tak, jak wspomniałem i teraz to nie robi absolutnie żadnego sprawdzania autoryzacji. Anonimowi użytkownicy mogą teraz łączyć się z każdą metodą ozdobioną tym niestandardowym atrybutem ... 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łych ciągów tak:

public static class CustomRoles 
{ 
    public const string Admin = "Admin"; 
    // and so on.. 
} 
+0

Wartościowy; ale to powinien być komentarz; nie jest odpowiedzią. – GhostCat

+1

Proste i eleganckie rozwiązanie! –

0

czuję się jak atrybut niestandardowy autoryzacji jest przesadą dla tej kwestii, chyba że masz dużą ilość ról.

Ponieważ łańcuch musi być znany w czasie kompilacji, dlaczego nie zrobić statyczną klasę roli, która zawiera ciągi publiczne role zostały zdefiniowane, a następnie dodać oddzielonych przecinkami ciągi z pewnych ról, które chcesz zezwolić na:

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

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

a następnie można użyć atrybutu Autoryzuj tak jak w klasie kontrolera lub metoda sterowania (lub oba):

[Authorize(Roles = Roles.ADMIN] 
public class ExampleController : Controller 
{ 
    [Authorize(Roles = Roles.ADMIN_OR_VIEWER) 
    public ActionResult Create() 
    { 
     ..code here... 
    } 
} 
+0

Ten przykład nie działa, a przynajmniej nie tak, jak myślisz. Na przykład, podczas gdy nowa rola "ADMIN_OR_VIEWER" w akcji jest nadmiarowa, ponieważ nie będziesz mógł uzyskać dostępu do metody "Create", jeśli nie masz jeszcze roli "ADMIN". W tym przypadku 'VIEWER' nigdy nie będzie mógł wywołać metody' Create'. –

+0

To rozwiązanie również nie jest skalowalne. Będzie punkt, w którym masz zbyt wiele ról z różnymi działaniami i nie powinieneś tworzyć każdej kombinacji – EduLopez

0

co zrobiłem jest odpowiedzią na @Tieson

Poprawiam trochę w jego odpowiedzi. Zamiast string.Join dlaczego nie przekonwertować go do listy?

Oto moja odpowiedź:

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

a następnie sprawdzić, czy rola jest ważna nadpisywanie 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 tam masz, to jest teraz stwierdzenia prawidłowości jeśli rola jest upoważniony do uzyskać dostęp do zasobów

Powiązane problemy