asp.net-mvc asp.net mvc 用多个枚举装饰 [Authorize()]

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1148312/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-07 23:43:33  来源:igfitidea点击:

asp.net mvc decorate [Authorize()] with multiple enums

asp.net-mvcauthorizationroles

提问by codette

I have a controller and I want two roles to be able to access it. 1-admin OR 2-moderator

我有一个控制器,我希望有两个角色能够访问它。1 位管理员或 2 位版主

I know you can do [Authorize(Roles="admin, moderators")] but I have my roles in an enum. With the enum I can only authorize ONE role. I can't figure out how to authorize two.

我知道你可以做 [Authorize(Roles="admin, moderators")] 但我在枚举中有我的角色。使用枚举我只能授权一个角色。我不知道如何授权两个。

I have tried something like [Authorize(Roles=MyEnum.Admin, MyEnum.Moderator)] but that wont compile.

我尝试过类似 [Authorize(Roles=MyEnum.Admin, MyEnum.Moderator)] 的方法,但无法编译。

Someone once suggested this:

曾经有人这样建议:

 [Authorize(Roles=MyEnum.Admin)]
 [Authorize(MyEnum.Moderator)]
 public ActionResult myAction()
 {
 }

but it doesn't work as an OR. I think in this case the user has to be part of BOTH roles. Am I overlooking some syntax? Or is this a case where I have to roll my own custom authorization?

但它不能作为 OR 工作。我认为在这种情况下,用户必须同时成为两个角色的一部分。我是否忽略了一些语法?或者这是我必须推出自己的自定义授权的情况?

采纳答案by CalebHC

Try using the bit OR operator like this:

尝试使用位 OR 运算符,如下所示:

[Authorize(Roles= MyEnum.Admin | MyEnum.Moderator)]
public ActionResult myAction()
{
}

If that doesn't work, you could just roll your own. I currently just did this on my project. Here's what I did:

如果这不起作用,你可以自己动手。我目前只是在我的项目中做到了这一点。这是我所做的:

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's role here

        // Perform a bitwise operation to see if the user's 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() {}

Also, make sure to add a flags attribute to your enum and make sure they are all valued from 1 and up. Like this:

此外,请确保为您的枚举添加一个标志属性,并确保它们的值都从 1 起。像这样:

[Flags]
public enum Roles
{
    Admin = 1,
    Moderator = 1 << 1,
    Newbie = 1 << 2
    etc...
}

The left bit shifting gives the values 1, 2, 4, 8, 16 and so on.

左位移位给出值 1、2、4、8、16 等。

Well, I hope this helps a little.

好吧,我希望这会有所帮助。

回答by Zaid Masud

Here is a simpleand elegant solution which allows you to simply use the following syntax:

这是一个简单而优雅的解决方案,它允许您简单地使用以下语法:

[AuthorizeRoles(MyEnum.Admin, MyEnum.Moderator)]

When creating your own attribute, use the paramskeyword in your constructor:

创建自己的属性时,请params在构造函数中使用关键字:

public class AuthorizeRoles : AuthorizeAttribute
{
    public AuthorizeRoles(params MyEnum[] roles)
    {
        ...
    }
    protected override bool AuthorizeCore(HttpContextBase httpContext)
    {
        ...
    }
}

This will allow you to use the attribute as follows:

这将允许您按如下方式使用该属性:

[AuthorizeRoles(MyEnum.Admin, MyEnum.Moderator)]
public ActionResult myAction()
{
}

回答by borigas

I combined a few of the solutions here to create my personal favorite. My custom attribute just changes the data to be in the form that SimpleMembership expects and lets it handle everything else.

我在这里结合了一些解决方案来创建我个人的最爱。我的自定义属性只是将数据更改为 SimpleMembership 期望的形式,并让它处理其他所有事情。

My roles enum:

我的角色枚举:

public enum MyRoles
{
    Admin,
    User,
}

To create roles:

创建角色:

public static void CreateDefaultRoles()
{
    foreach (var role in Enum.GetNames(typeof(MyRoles)))
    {
       if (!Roles.RoleExists(role))
       {
            Roles.CreateRole(role);
        }
    }
}

Custom attribute:

自定义属性:

public class AuthorizeRolesAttribute : AuthorizeAttribute
{
    public AuthorizeRolesAttribute(params MyRoles[] allowedRoles)
    {
        var allowedRolesAsStrings = allowedRoles.Select(x => Enum.GetName(typeof(MyRoles), x));
        Roles = string.Join(",", allowedRolesAsStrings);
    }
}

Used like so:

像这样使用:

[AuthorizeRoles(MyRoles.Admin, MyRoles.User)]
public ActionResult MyAction()
{
    return View();
}

回答by Jay

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

回答by meataxe

Here's my version, based on @CalebHC and @Lee Harold's answers.

这是我的版本,基于@CalebHC 和@Lee Harold 的回答。

I've followed the style of using named parameters in the attribute and overridden the base classes Rolesproperty.

我遵循了在属性中使用命名参数的风格并覆盖了基类Roles属性。

@CalebHC's answer uses a new Isproperty which I think is unnecessary, because AuthorizeCore()is overridden (which in the base class uses Roles) so it makes sense to use our own Rolesas well. By using our own Roleswe get to write Roles = Roles.Adminon the controller, which follows the style of other .Net attributes.

@CalebHC 的回答使用了一个Is我认为不必要的新属性,因为AuthorizeCore()它被覆盖(在基类中使用角色),所以使用我们自己Roles的也是有意义的。通过使用我们自己的,Roles我们可以Roles = Roles.Admin在控制器上编写,它遵循其他 .Net 属性的样式。

I've used two constructors to CustomAuthorizeAttributeto show real active directory group names being passed in. In production I use the parameterised constructor to avoid magic strings in the class: group names are pulled from web.config during Application_Start()and passed in on creation using a DI tool.

我使用了两个构造函数来CustomAuthorizeAttribute显示传入的真实活动目录组名。在生产中,我使用参数化构造函数来避免类中的魔术字符串:组名Application_Start()在创建期间从 web.config 中提取,并在创建时使用 DI 传入工具。

You'll need a NotAuthorized.cshtmlor similar in your Views\Sharedfolder or unauthorized users will get an error screen.

NotAuthorized.cshtmlViews\Shared文件夹中需要一个或类似的文件,否则未经授权的用户将看到错误屏幕。

Here is the code for the base class AuthorizationAttribute.cs.

这是基类AuthorizationAttribute.cs的代码。

Controller:

控制器:

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

回答by Lee Harold

To add to CalebHC's code and answer ssmith's question about handling users who have multiple roles...

要添加到 CalebHC 的代码并回答 ssmith 关于处理具有多个角色的用户的问题...

Our custom security principal returns a string array representing all the groups/roles that a user is in. So first we have to convert all the strings in the array that match items in the enum. Finally, we look for any match - if so, then the user is authorized.

我们的自定义安全主体返回一个字符串数组,表示用户所在的所有组/角色。因此,首先我们必须转换数组中与枚举中的项目匹配的所有字符串。最后,我们寻找任何匹配项——如果是,则用户被授权。

Note that we're also redirecting an unauthorized user to a custom "NotAuthorized" view.

请注意,我们还将未经授权的用户重定向到自定义的“NotAuthorized”视图。

The whole class looks like this:

整个班级是这样的:

[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" };
    } 
}

回答by Lee Harold

Or you could concatenate like:

或者你可以像这样连接:

[Authorize(Roles = Common.Lookup.Item.SecurityRole.Administrator + "," + Common.Lookup.Item.SecurityRole.Intake)]