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

发布于 2024-07-27 19:36:55 字数 460 浏览 5 评论 0原文

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

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

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

有人曾经提出过这个建议:

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

但它不能作为 OR 起作用。 我认为在这种情况下,用户必须同时属于这两个角色。 我忽略了一些语法吗? 或者在这种情况下我必须滚动自己的自定义授权?

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

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.

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

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?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(9

触ぅ动初心 2024-08-03 19:36:56

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

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

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

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

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

以下是基类 AuthorizationAttribute.cs 的代码。

控制器:

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

[CustomAuthorize(Roles = Roles.Admin)]
public ActionResult About()
{
  return this.View();
}

自定义授权属性:

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

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

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

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

I've used two constructors to CustomAuthorizeAttribute to 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.

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

Here is the code for the base class 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;
  }    
}
旧瑾黎汐 2024-08-03 19:36:56

要添加到 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" };
    } 
}

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

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.

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" };
    } 
}
笑忘罢 2024-08-03 19:36:56

我不完全同意之前答案的复杂性。 这是我的版本,使用字典将角色枚举值转换为字符串。

当然,每当引入新角色时,就需要更新映射。

public class AuthorizeRoles : AuthorizeAttribute
{
    private Dictionary<Roles, string> _rolesMap = new()
    {
        // Fully qualified name since Roles already exist in AuthorizeAttribute
        { Common.Enums.Roles.User, "CustomRoles.User" },
        { Common.Enums.Roles.Manager, "CustomRoles.Manager" },
        { Common.Enums.Roles.Admin, "CustomRoles.Admin" },
    };
    
    public AuthorizeRoles(params Roles[] roles)
    {
        Roles = string.Join(",", roles.Select(role => _rolesMap[role]));
    }
}

可以在这样的控制器中使用

[AuthorizeRoles(Roles.User, Roles.Manager, Roles.Admin)]

I did not fully agree with the complexity of previous answers. Here's my version using a dictionary to convert Role enum values to string.

Of course, the mapping needs to be updated whenever there is a new role introduced.

public class AuthorizeRoles : AuthorizeAttribute
{
    private Dictionary<Roles, string> _rolesMap = new()
    {
        // Fully qualified name since Roles already exist in AuthorizeAttribute
        { Common.Enums.Roles.User, "CustomRoles.User" },
        { Common.Enums.Roles.Manager, "CustomRoles.Manager" },
        { Common.Enums.Roles.Admin, "CustomRoles.Admin" },
    };
    
    public AuthorizeRoles(params Roles[] roles)
    {
        Roles = string.Join(",", roles.Select(role => _rolesMap[role]));
    }
}

Which can be used in the controllers like this

[AuthorizeRoles(Roles.User, Roles.Manager, Roles.Admin)]
流云如水 2024-08-03 19:36:56

或者你可以像这样连接:

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

Or you could concatenate like:

[Authorize(Roles = Common.Lookup.Item.SecurityRole.Administrator + "," + Common.Lookup.Item.SecurityRole.Intake)]
别把无礼当个性 2024-08-03 19:36:55

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

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

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

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

When creating your own attribute, use the params keyword in your constructor:

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()
{
}
2024-08-03 19:36:55

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

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

另外,请确保向您的枚举添加一个 flags 属性,并确保它们的值都从 1 开始。 像这样:

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

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

嗯,我希望这会有所帮助。

Try using the bit OR operator like this:

[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:

[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.

Well, I hope this helps a little.

岁月染过的梦 2024-08-03 19:36:55

我在这里结合了一些解决方案来创建我个人最喜欢的解决方案。 我的自定义属性只是将数据更改为 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();
}

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.

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();
}
不醒的梦 2024-08-03 19:36:55

要使用多个枚举来装饰 [Authorize()],您可以编写如下代码:

[Authorize(Roles = nameof(MyEnum.Admin) + "," + nameof(MyEnum.Moderator))]

您也可以为单个角色编写此代码:

[Authorize(Roles = nameof(MyEnum.Admin))]

To decorate [Authorize()] with multiple enums, you can write code just like this:

[Authorize(Roles = nameof(MyEnum.Admin) + "," + nameof(MyEnum.Moderator))]

You are also can write this code for single role:

[Authorize(Roles = nameof(MyEnum.Admin))]
慵挽 2024-08-03 19:36:55

尝试

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

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();
    }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文