Авторизуйте атрибути за допомогою декількох ролей


97

Я хотів би додати авторизацію до контролера для кількох ролей одночасно.

Зазвичай це виглядатиме так:

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

Але я зберігаю свої ролі в конкурсах, оскільки вони можуть колись змінитися або продовжитися.

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

Я не можу цього зробити, оскільки рядок повинен бути відомий під час компіляції:

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

Чи є спосіб обійти проблему?

Я міг би написати const, який просто містить "RoleA, RoleB, RoleC" - але мені не подобаються магічні рядки, і це магічний рядок. Зміна назви Ролі та забуття змінити комбінований рядок було б катастрофою.

Я використовую MVC5. Ідентичність і роль ASP.NET відомі під час компіляції.


ви використовуєте public const string RoleA = "RoleA"; або як ви вже писали питання?
Мукеш Модхвадія

Відповіді:


188

Спробуйте створити спеціальний атрибут авторизації на зразок цього .

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

Припускаючи, що ваші ролі будуть однаковими для декількох контролерів, створіть допоміжний клас:

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

Потім використовуйте його так:

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

12
Зараз це ідея, гідна Мака Гівера;)
Крістіан Зауер

2
Дуже приємне рішення :)
aup

1
Мені також дуже подобається це рішення, особливо тому, що я можу дозволити моїй ролі бути переліком, а не рядком. Яким буде хороший простір імен та розташування в ієрархії проекту для розміщення цього спеціального атрибута авторизації?
Simon Shine

4
Я не впевнений, що тут відбувається, але це НЕ допомогло мені, будь-який користувач незалежно від ролі мав доступ до методу.
Урієльцен

2
Та сама проблема, що і @Urielzen, але вона була виправлена ​​відповіді нижче від Джеррі Фінегана (використовуючи "System.Web.Mvc.AuthorizeAttribute, а не System.Web.Http.AuthorizeAttribute")
RJB

13

Переконайтеся, що ви виводите свій власний клас атрибутів, System.Web.Mvc.AuthorizeAttributeа НЕ System.Web.Http.AuthorizeAttribute.

Я зіткнувся з тією ж проблемою. Як тільки я його змінив, все спрацювало.

Можливо, ви також захочете додати наступне до власного класу атрибутів:

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

Я щойно спробував це і знайшов посилання на бібліотеку System.Web.Http.AuthorizeAttributeINSTEAD OFSystem.Web.Mvc.AuthorizeAttribute
фразер jordan

10

Найкращий і найпростіший спосіб вирішити цю проблему - це просто об'єднати ролі в атрибуті "Авторизувати".

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

з CustomRole клас з постійними рядками, як це:

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

2
Цінні; але це має бути коментар; не відповідь.
GhostCat

1
Просте і елегантне рішення!
Іосіф Банчою

Як ваша відповідь, так і прийнята відповідь спричинять авторизацію, якщо її правильно застосувати (я використовую прийняту у виробничій веб-програмі). Пропоную редагувати, щоб видалити коментарі щодо прийнятої відповіді.
Ерік Ескільдсен

3

Що я зробив, так це відповідь у @Tieson

Я трохи підправляю його відповідь. Замість string.Join, чому б не перетворити його в список?

Ось моя відповідь:

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

А потім перевірте, чи дійсна роль замінює 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);
            }
        }

І ось у вас це є, зараз воно перевіряється, якщо роль авторизована для доступу до ресурсу


1

Я відчуваю, що спеціальний атрибут авторизації є надмірним для цієї проблеми, якщо у вас немає великої кількості ролей.

Оскільки рядок повинен бути відомий під час компіляції, чому б не створити статичний клас ролі, який містить загальнодоступні рядки визначених вами ролей, а потім додати рядки, розділені комами, з певними ролями, які ви хочете авторизувати:

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

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

І тоді ви можете використовувати атрибут авторизації, як так, у класі контролера або методі контролера (або обох):

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

1
Цей приклад не працює, або, принаймні, не так, як ви можете подумати. Наприклад, хоча роман, ADMIN_OR_VIEWERроль у дії є зайвою, оскільки вам не дозволять потрапити до Createметоду, якщо у вас ще немає ADMINролі. У цьому випадку VIEWERніколи не вдасться викликати Createметод.
Джон Лейдегрен

Це рішення також не є масштабним. Там буде момент, коли у вас занадто багато ролей з різними діями, і вам не варто створювати кожну комбінацію
EduLopez
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.