36

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

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

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

曾经有人这样建议:

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

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

4

7 回答 7

42

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

[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()
{
}
于 2012-01-05T15:20:51.493 回答
32

尝试像这样使用位 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'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() {}

此外,请确保向您的枚举添加一个标志属性,并确保它们的值都从 1 及以上开始。像这样:

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

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

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

于 2009-07-18T19:33:22.197 回答
12

我在这里结合了一些解决方案来创建我个人的最爱。我的自定义属性只是将数据更改为 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();
}
于 2013-02-11T18:21:49.253 回答
2

尝试

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();
    }
}
于 2012-05-04T09:06:25.870 回答
1

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

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

@CalebHC 的答案使用了一个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();
}

自定义授权属性:

// 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;
  }    
}
于 2012-10-17T23:20:53.160 回答
0

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

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

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

整个班级看起来像这样:

[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" };
    } 
}
于 2011-09-15T20:31:40.140 回答
-1

或者你可以像这样连接:

[Authorize(Roles = Common.Lookup.Item.SecurityRole.Administrator + "," + Common.Lookup.Item.SecurityRole.Intake)]
于 2009-07-25T02:47:30.717 回答