Asp.net mvc decorate [Авторизовать()] с несколькими перечислениями
У меня есть контроллер, и я хочу, чтобы две роли имели к нему доступ. 1-admin ИЛИ 2-модератор
Я знаю, что вы можете сделать [Authorize (Roles = "admin, moderators" )], но у меня есть роли в перечислении. С перечислением я могу разрешать только одну роль. Я не могу понять, как разрешить два.
Я пробовал что-то вроде [Authorize (Roles = MyEnum.Admin, MyEnum.Moderator)], но это не скомпилируется.
Кто-то однажды предложил это:
[Authorize(Roles=MyEnum.Admin)]
[Authorize(MyEnum.Moderator)]
public ActionResult myAction()
{
}
но он не работает как OR. Я думаю, что в этом случае пользователь должен быть частью роли BOTH. Могу ли я игнорировать некоторый синтаксис? Или это случай, когда мне приходится откатывать собственное пользовательское разрешение?
Ответы
Ответ 1
Попробуйте использовать оператор бит OR следующим образом:
[Authorize(Roles= MyEnum.Admin | MyEnum.Moderator)]
public ActionResult myAction()
{
}
Если это не сработает, вы можете просто свернуть свое. Я только что сделал это в своем проекте. Вот что я сделал:
public class AuthWhereRole : AuthorizeAttribute
{
/// <summary>
/// Add the allowed roles to this property.
/// </summary>
public UserRole Is;
/// <summary>
/// Checks to see if the user is authenticated and has the
/// correct role to access a particular view.
/// </summary>
/// <param name="httpContext"></param>
/// <returns></returns>
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
if (httpContext == null)
throw new ArgumentNullException("httpContext");
// Make sure the user is authenticated.
if (!httpContext.User.Identity.IsAuthenticated)
return false;
UserRole role = someUser.Role; // Load the user role here
// Perform a bitwise operation to see if the user role
// is in the passed in role values.
if (Is != 0 && ((Is & role) != role))
return false;
return true;
}
}
// Example Use
[AuthWhereRole(Is=MyEnum.Admin|MyEnum.Newbie)]
public ActionResult Test() {}
Кроме того, обязательно добавьте атрибут flags в свой список и убедитесь, что все они оценены от 1 и выше. Вот так:
[Flags]
public enum Roles
{
Admin = 1,
Moderator = 1 << 1,
Newbie = 1 << 2
etc...
}
Левое смещение бит дает значения 1, 2, 4, 8, 16 и т.д.
Хорошо, я надеюсь, что это поможет немного.
Ответ 2
Вот простое и элегантное решение, которое позволяет вам просто использовать следующий синтаксис:
[AuthorizeRoles(MyEnum.Admin, MyEnum.Moderator)]
Создавая свой собственный атрибут, используйте params
ключевое слово в своем конструкторе:
public class AuthorizeRoles : AuthorizeAttribute
{
public AuthorizeRoles(params MyEnum[] roles)
{
...
}
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
...
}
}
Это позволит вам использовать атрибут следующим образом:
[AuthorizeRoles(MyEnum.Admin, MyEnum.Moderator)]
public ActionResult myAction()
{
}
Ответ 3
Я объединил несколько решений здесь, чтобы создать свой личный фаворит. Мой пользовательский атрибут просто изменяет данные в форме, которую ожидает SimpleMembership, и позволяет ему обрабатывать все остальное.
Мои роли перечислены:
public enum MyRoles
{
Admin,
User,
}
Чтобы создать роли:
public static void CreateDefaultRoles()
{
foreach (var role in Enum.GetNames(typeof(MyRoles)))
{
if (!Roles.RoleExists(role))
{
Roles.CreateRole(role);
}
}
}
Пользовательский атрибут:
public class AuthorizeRolesAttribute : AuthorizeAttribute
{
public AuthorizeRolesAttribute(params MyRoles[] allowedRoles)
{
var allowedRolesAsStrings = allowedRoles.Select(x => Enum.GetName(typeof(MyRoles), x));
Roles = string.Join(",", allowedRolesAsStrings);
}
}
Используется так:
[AuthorizeRoles(MyRoles.Admin, MyRoles.User)]
public ActionResult MyAction()
{
return View();
}
Ответ 4
Try
public class CustomAuthorize : AuthorizeAttribute
{
public enum Role
{
DomainName_My_Group_Name,
DomainName_My_Other_Group_Name
}
public CustomAuthorize(params Role[] DomainRoles)
{
foreach (var domainRole in DomainRoles)
{
var domain = domainRole.ToString().Split('_')[0] + "_";
var role = domainRole.ToString().Replace(domain, "").Replace("_", " ");
domain=domain.Replace("_", "\\");
Roles += ", " + domain + role;
}
Roles = Roles.Substring(2);
}
}
public class HomeController : Controller
{
[CustomAuthorize(Role.DomainName_My_Group_Name, Role.DomainName_My_Other_Group_Name)]
public ActionResult Index()
{
return View();
}
}
Ответ 5
Здесь моя версия, основанная на @CalebHC и @Lee Harold, отвечает.
Я следил за стилем использования именованных параметров в атрибуте и переопределял свойства базовых классов Roles
.
@CalebHC answer использует новое свойство Is
, которое, как мне кажется, не нужно, потому что AuthorizeCore()
переопределяется (который в базовом классе использует роли), поэтому имеет смысл использовать наш собственный Roles
. Используя наш собственный Roles
, мы можем написать Roles = Roles.Admin
на контроллере, который следует за стилем других атрибутов .Net.
Я использовал два конструктора для CustomAuthorizeAttribute
для отображения реальных активных имен групп каталогов. В производстве я использую параметризованный конструктор, чтобы избежать магических строк в классе: имена групп вытягиваются из web.config во время Application_Start()
и передается при создании с помощью инструмента DI.
Вам понадобится NotAuthorized.cshtml
или подобное в вашей папке Views\Shared
, или неавторизованные пользователи получат экран с ошибкой.
Вот код для базового класса AuthorizationAttribute.cs.
Контроллер:
public ActionResult Index()
{
return this.View();
}
[CustomAuthorize(Roles = Roles.Admin)]
public ActionResult About()
{
return this.View();
}
CustomAuthorizeAttribute:
// The left bit shifting gives the values 1, 2, 4, 8, 16 and so on.
[Flags]
public enum Roles
{
Admin = 1,
User = 1 << 1
}
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = true)]
public class CustomAuthorizeAttribute : AuthorizeAttribute
{
private readonly string adminGroupName;
private readonly string userGroupName;
public CustomAuthorizeAttribute() : this("Domain Admins", "Domain Users")
{
}
private CustomAuthorizeAttribute(string adminGroupName, string userGroupName)
{
this.adminGroupName = adminGroupName;
this.userGroupName = userGroupName;
}
/// <summary>
/// Gets or sets the allowed roles.
/// </summary>
public new Roles Roles { get; set; }
/// <summary>
/// Checks to see if the user is authenticated and has the
/// correct role to access a particular view.
/// </summary>
/// <param name="httpContext">The HTTP context.</param>
/// <returns>[True] if the user is authenticated and has the correct role</returns>
/// <remarks>
/// This method must be thread-safe since it is called by the thread-safe OnCacheAuthorization() method.
/// </remarks>
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
if (httpContext == null)
{
throw new ArgumentNullException("httpContext");
}
if (!httpContext.User.Identity.IsAuthenticated)
{
return false;
}
var usersRoles = this.GetUsersRoles(httpContext.User);
return this.Roles == 0 || usersRoles.Any(role => (this.Roles & role) == role);
}
protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
if (filterContext == null)
{
throw new ArgumentNullException("filterContext");
}
filterContext.Result = new ViewResult { ViewName = "NotAuthorized" };
}
private IEnumerable<Roles> GetUsersRoles(IPrincipal principal)
{
var roles = new List<Roles>();
if (principal.IsInRole(this.adminGroupName))
{
roles.Add(Roles.Admin);
}
if (principal.IsInRole(this.userGroupName))
{
roles.Add(Roles.User);
}
return roles;
}
}
Ответ 6
Чтобы добавить к CalebHC код и ответ на вопрос ssmith об обработке пользователей, у которых есть несколько ролей...
Наш пользовательский принцип безопасности возвращает массив строк, представляющий все группы/роли, в которых находится пользователь. Поэтому сначала мы должны преобразовать все строки в массиве, соответствующие элементам перечисления. Наконец, мы ищем любое совпадение - если это так, то пользователь авторизован.
Обратите внимание, что мы также перенаправляем неавторизованного пользователя в пользовательское представление "NotAuthorized".
Весь класс выглядит следующим образом:
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = true)]
public class CustomAuthorizeAttribute : AuthorizeAttribute
{
/// <summary>
/// Add the allowed roles to this property.
/// </summary>
public Roles Is { get; set; }
/// <summary>
/// Checks to see if the user is authenticated and has the
/// correct role to access a particular view.
/// </summary>
/// <param name="httpContext"></param>
/// <returns></returns>
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
if (httpContext == null)
throw new ArgumentNullException("httpContext");
if (!httpContext.User.Identity.IsAuthenticated)
return false;
var iCustomPrincipal = (ICustomPrincipal) httpContext.User;
var roles = iCustomPrincipal.CustomIdentity
.GetGroups()
.Select(s => Enum.Parse(typeof (Roles), s))
.ToArray();
if (Is != 0 && !roles.Cast<Roles>().Any(role => ((Is & role) == role)))
{
return false;
}
return true;
}
protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
if (filterContext == null)
throw new ArgumentNullException("filterContext");
filterContext.Result = new ViewResult { ViewName = "NotAuthorized" };
}
}
Ответ 7
Или вы можете объединиться, например:
[Авторизовать (Роли = Common.Lookup.Item.SecurityRole.Administrator + "," + Common.Lookup.Item.SecurityRole.Intake)]