Разрешить нескольким ролям доступ к действию контроллера

274

Прямо сейчас я украшаю метод, подобный этому, чтобы позволить "членам" получить доступ к моему действию контроллера

[Authorize(Roles="members")]

Как мне разрешить более одной роли? Например, следующее не работает, но показывает, что я пытаюсь сделать (разрешить доступ "members" и "admin"):

[Authorize(Roles="members", "admin")] 
codette
источник
4
Пожалуйста, измените принятый ответ на этот вопрос. Человек с текущим принятым ответом отредактировал его, указав, что он был неправ.
Эрик Дж.

Ответы:

595

Другой вариант - использовать один фильтр авторизации, когда вы отправили, но удалить внутренние цитаты.

[Authorize(Roles="members,admin")]
Джим Шмехил
источник
5
Работает в MVC 5 тоже. +1
gkonuralp
4
Работает в ASP.NET Core 1.0 (MVC 6) и Microsoft.AspNet.Identity v3. *
Сорен
3
Это нормально, если у вас есть только один контроллер, который необходимо авторизовать. Если у вас их больше одного, вы дублируете эти строковые константы (гадость). Я предпочитаю статический класс с именами ролей. Моя домашняя ненависть - это дублирующиеся строки ... так плохо.
Робник
1
@kraeg хорошая новость, что вы решили свою проблему. Теперь рассмотрите возможность удаления ваших комментариев, пожалуйста
Пабло Клаус
1
Зачем? Мне потребовались годы, чтобы решить это. Это может быть полезно для кого-то еще, испытывающего ту же проблему.
Крег
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 ролей?
Kiquenet
Я думаю, что ваша проблема заключается в моделировании разрешений помимо того, что можно сделать с помощью .net
Пабло Клаус
82

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

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

Использование:

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

Семантически это совпадает с ответом Джима Шмехила.

Михкель Мюйр
источник
3
Это не работает для меня, вошедший в систему пользователь смог обойти атрибут, даже если у пользователя не было ни одной из ролей.
Уриэльзен
9
Этот ответ лучше подходит для
случаев,
3
это лучший ответ
IgorShch
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 Фабрицио Мартинеса Тамайо https://github.com/fabriciomrtnz/FNHMVC/

Бернардо Лорейро
источник
1
Ваш метод OnAuthorization потребует от пользователя иметь все перечисленные роли; Это было намеренно, или вы пропустили разрыв в этом цикле?
Tieson T.
@Tieson: Я проверял, что довольно близко, определенно кажется, что в этом цикле потребуется перерыв.
OcelotXL
@TiesonT. и @ madrush, я ценю ваше исправление, оно действительно может иметь разрыв внутри цикла. Я изменю код выше.
Бернардо Лорейро
В перечислениях UserRoles приятно. Вы объявляете это вручную или оно генерируется автоматически на основе содержимого БД?
Конрад Вилтерстен
@KonradViltersten Это вручную, но я думаю, что с помощью класса Reflection и Dynamic можно автоматически сгенерировать класс
Бернардо Лоурейро
3

Другое ясное решение: вы можете использовать константы для соблюдения соглашения и добавления нескольких атрибутов [Authorize]. Проверь это:

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атрибутов используют семантику AND и требуют выполнения ВСЕХ условий (т. Е. Пользователь должен быть в роли администратора и гостя).
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;
    }
}
GER
источник
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();
}
Kinzzy Goel
источник
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
Ответы, включая код, должны иметь как минимум минимальное описание, объясняющее, как работает код и почему он отвечает на вопрос. В дальнейшем режим форматирования раздела кода нуждается в улучшении.
Роберто Кабони
А? @ Орсит Моэль, похоже, скопирован в неправильную ветку ...
Кэмерон Форвард