Дозволити декілька ролей для доступу до дій контролера


274

Зараз я прикрашаю такий метод, щоб дозволити "членам" отримати доступ до моєї дії контролера

[Authorize(Roles="members")]

Як дозволити кілька ролей? Наприклад, наступне не працює, але воно показує, що я намагаюся зробити (дозволити "членам" та "адміністратору" доступ):

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

4
Будь ласка, змініть прийняту відповідь на це запитання. Людина із прийнятою на даний момент відповіддю редагувала її, вказуючи на те, що він помилявся.
Ерік Дж.

Відповіді:


595

Інший варіант - використовувати один фільтр авторизації під час публікації, але видалити внутрішні цитати.

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

5
Працює і в MVC 5. +1
gkonuralp

4
Працює в ASP.NET Core 1.0 (MVC 6) та Microsoft.AspNet.Identity v3. *
Soren

3
Це нормально, якщо у вас є лише один контролер, який потрібно авторизувати. Якщо у вас їх більше, ви дублюєте ці константи рядків (yuck). Я дуже віддаю перевагу статичному класу, який має назви ролей. Моя ненависть до домашніх улюбленців - це дублікати струн ... так це погано.
robnick

1
@kraeg хороша новина про те, що ви вирішили свою проблему. Тепер, подумайте про видалення ваших коментарів, будь ласка
Пабло Клаус

1
Чому? На це мені знадобилися віки. Це може бути корисним для когось іншого, який зіткнувся з тією ж проблемою.
kraeg

129

Якщо ви хочете використовувати спеціальні ролі, ви можете зробити це:

CustomRoles клас:

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

Використання

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

Якщо у вас кілька ролей, можливо, ви можете їх поєднати (для наочності) так:

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

Використання

[Authorize(Roles = CustomRoles.AdministratorOrUser)]

7
Це було б гарною відповіддю, якби ви пояснили людям, які не знали, що за CustomRoles.
Джеймс Скемп

1
@JamesSkemp добре, я продовжив свою відповідь. Це дуже просто. CustumRoles - це клас, який я створив, який містить деякі константи, які відповідають моїм ролям програми. Я зробив це з кількох причин: 1) Це дозволяє використовувати intellisense, щоб уникнути орфографічних помилок 2) спростити технічне обслуговування. Якщо роль зміниться, мені доведеться оновити лише одне місце в моїй програмі.
Пабло Клаус

@Pabloker Альтернативно, ви можете створити перерахунок з атрибутом Flags, наприклад Convert.ToString (CustomRoles.Administrator | CustomRoles.User); - прикрою частиною є те, що для цього потрібне явне перетворення
cstruter

Якщо у вас 39 ролей?
Кікенет

Я думаю, що ваша проблема полягає в моделюванні дозволів, що перевищує те, що можна зробити з .net
Пабло Клаус

82

Одним із можливих спрощень було б підклас AuthorizeAttribute:

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

Використання:

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

Семантично це те саме, що відповідь Джима Шмехіла.


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

9
Ця відповідь краще, коли ви використовуєте константи як свої значення: тобто [Ролі (Constants.Admin, Constants.Owner)]
dalcam

3
це найкраща відповідь
IgorSchch

18

Для MVC4, використовуючи Enum( UserRoles) для моїх ролей, я використовую звичай AuthorizeAttribute.

На моїй контрольованій дії я роблю:

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

І я використовую такий звичай 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;
        }
    }
}

Це частина модифікованого FNHMVC від Fabricio Martínez Tamayo https://github.com/fabriciomrtnz/FNHMVC/


1
Ваш метод OnAuthorization вимагає від користувача всіх перерахованих ролей; це було навмисно, чи вам не вистачає перерви в цій петлі?
Тієсон Т.

@Tieson: Я перевірив це досить уважно, напевно, здається, потрібен перерва в цьому циклі.
OcelotXL

@TiesonT. і @ madrush, я ціную ваше виправлення, воно дійсно могло б перерватись у циклі. Я зміню код вище.
Бернардо Лурейро

У перерахування UserRoles приємно. Ви декларуєте це вручну чи він автоматично генерується на основі вмісту БД?
Конрад Вільтерстен

@KonradViltersten Це вручну, але, мабуть, з автогенерацією класу Reflection і Dynamic це можна зробити
Бернардо Лурейро

3

Ще одне чітке рішення: ви можете використовувати константи, щоб дотримуватися конвенції та додавати кілька атрибутів [Авторизувати]. Заціни:

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

Потім у контролері:

[Authorize(Roles = RolesConvention.Administrator )]
[Authorize(Roles = RolesConvention.Guest)]
[Produces("application/json")]
[Route("api/[controller]")]
public class MyController : Controller

14
Кілька Authorizeатрибутів використовують І семантику і вимагають дотримання ВСІХ умов (тобто користувач повинен виконувати як функції адміністратора, так і гостя).
trousyt

3

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

using System.Web.Mvc;

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

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

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

1

Кращий код з додаванням підкласу 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 }
                  }
              );
            }
        }
    }

Як це використовувати

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

public ActionResult Index()
{
return View();
}

1

Використовуючи AspNetCore 2.x, ви повинні піти трохи іншим шляхом:

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

просто використовуйте його так:

[Authorize(YourEnum.Role1, YourEnum.Role2)]

-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);

1
Відповіді, що включають код, повинні мати хоча б мінімальну характеристику, що пояснює, як працює код і чому він відповідає на питання. Далівизначте форматування розділу коду, щоб його покращити.
Роберто Кабоні

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