具有多个角色的授权属性

113

我希望为一个控制器同时添加多个角色的授权。

通常情况下,这会像这样:

[Authorize(Roles = "RoleA,RoleB,RoleC")]
public async Task<ActionResult> Index()
{
}

但是我已经将我的角色存储在常量中,因为它们可能会在某个时候更改或扩展。

public const RoleA = "RoleA";
public const RoleB = "RoleB";
public const RoleC = "RoleC";

我无法做到这一点,因为该字符串必须在编译时已知:

[Authorize(Roles = string.join(",",RoleA,RoleB,RoleC)]
public async Task<ActionResult> Index()
{
}

有没有方法可以规避这个问题?

我可以写一个常量,其中只包含“RoleA, RoleB, RoleC”-但我不喜欢魔术字符串,而且这是一个魔术字符串。改变角色名称并忘记更改组合字符串的名称将是一场灾难。

我正在使用MVC5、ASP.NET身份验证和角色,这些在编译时已知。


你是在使用公共常量字符串RoleA = "RoleA"; 还是像你在问题中写的那样? - Mukesh Modhvadiya
1
可能是允许多个角色访问控制器操作的重复问题。 - Ryan Kohn
5个回答

226

尝试创建自定义的授权属性,就像这个示例。

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

如果您的多个控制器的角色相同,可以创建一个辅助类:

public static class Role
{
    public const string Administrator = "Administrator";
    public const string Assistant = "Assistant";
}

然后像这样使用它:

public class MyController : Controller
{
    [AuthorizeRoles(Role.Administrator, Role.Assistant)]
    public ActionResult AdminOrAssistant()
    {                       
        return View();
    }
}

14
这真是一个值得麦克盖尔思考的好主意 ;) - Christian Sauer
2
我非常喜欢这个解决方案,特别是因为我可以让我的角色成为一个枚举而不是一个字符串。在项目层次结构中,放置此自定义授权属性的良好命名空间和位置是什么? - sshine
@SimonShine - 我认为,你应该将它放在 Web 项目中,例如 Attributes 文件夹中。但是,如果您在一个解决方案中有多个项目,则需要将其移动到另一个库中,以便每个项目都可见。 - MacGyver
4
我不确定这里发生了什么,但这并没有帮助到我。无论用户角色如何,都可以访问该方法。 - Urielzen
2
与@Urielzen相同的问题,但是通过Jerry Finegan下面的答案得到了解决(使用“System.Web.Mvc.AuthorizeAttribute”而不是“System.Web.Http.AuthorizeAttribute”) - RJB
显示剩余3条评论

19

我发现解决这个问题最好、最简单的方法就是在授权属性中连接角色。

[Authorize(Roles = CustomRoles.Admin + "," + CustomRoles.OtherRole)]

使用CustomRole类,其中包括像这样的常量字符串:

public static class CustomRoles
{
    public const string Admin = "Admin";
    // and so on..
}

3
有价值的;但这应该是一条注释,而不是一个答案。 - GhostCat
如果正确实现,你的回答和被接受的回答都会触发授权(我正在生产 Web 应用中使用被接受的回答)。建议编辑以删除有关被接受的回答的评论。 - Eric Eskildsen
如果角色是枚举类型,那么你可以使用类似以下的代码: [Authorize(Roles = nameof(UserRoleEnum.User) + "," + nameof(UserRoleEnum.Admin))] - Varun

14

请确保您的自定义属性类是从 System.Web.Mvc.AuthorizeAttribute 而不是 System.Web.Http.AuthorizeAttribute 派生出来的。

我曾经遇到过同样的问题。一旦我改变了它,一切都正常了。

您可能还想将以下内容添加到您的自定义属性类中:

[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = true, AllowMultiple = true)] 

我刚刚尝试了一下,发现引用库System.Web.Http.AuthorizeAttribute而不是System.Web.Mvc.AuthorizeAttribute - fraser jordan

4

我所做的就是在 @Tieson 的答案中进行调整。为何不将其转换为列表,而不是使用 string.Join 呢?

以下是我的回答:

public class AuthorizeRolesAttribute : AuthorizeAttribute
{
    private new List<string> Roles;
    public AuthorizeRolesAttribute(params string[] roles) : base()
    {
        Roles = roles.toList()
    }
}

然后在 OnAuthorization 方法中检查角色是否有效。

public override void OnAuthorization(HttpActionContext actionContext)
{
            if (Roles == null)
                HandleUnauthorizedRequest(actionContext);
            else
            {
                ClaimsIdentity claimsIdentity = HttpContext.Current.User.Identity as ClaimsIdentity;
                string _role = claimsIdentity.FindFirst(ClaimTypes.Role).Value;
                bool isAuthorize = Roles.Any(role => role == _role);

                if(!isAuthorize)
                    HandleUnauthorizedRequest(actionContext);
            }
        }

现在它已经验证了角色是否被授权访问资源。


2

我觉得对于这个问题来说,自定义的授权属性有些过头了,除非你有大量的角色需要处理。

既然字符串必须在编译时知道,为什么不创建一个静态的Role类,其中包含你定义的角色的公共字符串,然后添加逗号分隔的字符串以授权特定的角色:

public static class Roles
{
    public const string ADMIN = "Admin";
    public const string VIEWER = "Viewer";

    public const string ADMIN_OR_VIEWER = ADMIN + "," + VIEWER;
}

然后您可以在控制器类或控制器方法(或两者)上使用Authorize属性,如下所示:

[Authorize(Roles = Roles.ADMIN]
public class ExampleController : Controller
{
    [Authorize(Roles = Roles.ADMIN_OR_VIEWER)
    public ActionResult Create()
    {
        ..code here...
    }
}

1
这个例子并不起作用,或者至少不是你想象中的那样。例如,虽然在操作上使用 ADMIN_OR_VIEWER 角色很新颖,但它是多余的,因为如果你没有 ADMIN 角色,就不允许你进入 Create 方法。在这种情况下,VIEWER 将永远无法调用 Create 方法。 - John Leidegren
这个解决方案也不具有可扩展性。当您拥有太多具有不同操作的角色时,会出现一些问题,您不应该创建每一个组合。 - EduLopez
@JohnLeidegren 这是不正确的。操作级别的AuthorizeAttribute会覆盖控制器级别的AuthorizeAttribute,否则,您将无法在带有AuthorizeAttribute的控制器中使用AllowAnonymousAttribute修饰的操作。 - Tom Lint

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接