授权具有多个角色的属性
Posted
技术标签:
【中文标题】授权具有多个角色的属性【英文标题】:Authorize Attribute with Multiple Roles 【发布时间】:2014-08-02 15:07:17 【问题描述】:我想为一个控制器添加授权,一次用于多个角色。
通常看起来像这样:
[Authorize(Roles = "RoleA,RoleB,RoleC")]
public async Task<ActionResult> Index()
但我已将我的角色存储在 const 中,因为它们可能会在某些时候更改或扩展。
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”的 const - 但我不喜欢魔术字符串,这是一个魔术字符串。更改角色的名称而忘记更改组合字符串将是一场灾难。
我正在使用 MVC5。 ASP.NET 标识和角色在编译时是已知的。
【问题讨论】:
你在使用 public const string RoleA = "RoleA";或者正如你所写的那样? allow multiple roles to access controller action 的可能重复项 【参考方案1】:尝试创建自定义授权属性,如this。
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();
【讨论】:
现在这个想法值得 Mac Gyver ;) 我也非常喜欢这个解决方案,特别是因为我可以让我的角色成为枚举而不是字符串。在项目层次结构中放置此自定义授权属性的良好命名空间和位置是什么? @SimonShine - 我相信,你应该把它放在 web 项目中,例如在属性文件夹中。但是,如果您在一个解决方案中有多个项目,则需要将其移至对每个项目可见的另一个库。 我不确定这里发生了什么,但这对我没有帮助,任何用户,无论角色如何,都可以访问该方法。 与@Urielzen 相同的问题,但已由 Jerry Finegan 的以下答案修复(使用“System.Web.Mvc.AuthorizeAttribute 而不是 System.Web.Http.AuthorizeAttribute”)【参考方案2】:确保您从System.Web.Mvc.AuthorizeAttribute
而非System.Web.Http.AuthorizeAttribute
派生自定义属性类。
我遇到了同样的问题。一旦我改变它,一切正常。
您可能还想将以下内容添加到您的自定义属性类中:
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = true, AllowMultiple = true)]
【讨论】:
我刚试过这个,发现引用了库System.Web.Http.AuthorizeAttribute
INSTEAD OF System.Web.Mvc.AuthorizeAttribute
【参考方案3】:
我发现解决此问题的最佳和最简单的方法就是在 Authorize 属性中连接角色。
[Authorize(Roles = CustomRoles.Admin + "," + CustomRoles.OtherRole)]
使用 CustomRole 的类具有像这样的常量字符串:
public static class CustomRoles
public const string Admin = "Admin";
// and so on..
【讨论】:
有价值的;但这应该是评论;不是答案。 如果正确实施,您的答案和接受的答案都将触发授权(我在生产网络应用程序中使用接受)。建议进行编辑以删除有关已接受答案的 cmets。 如果角色是枚举,那么您可以使用类似 [Authorize(Roles = nameof(UserRoleEnum.User) + "," + nameof(UserRoleEnum.Admin))]【参考方案4】:我觉得自定义授权属性对于这个问题来说是多余的,除非你有大量的角色。
既然字符串必须在编译时知道,为什么不创建一个静态 Role 类,其中包含您已定义的角色的公共字符串,然后添加逗号分隔的字符串与您想要授权的某些角色:
public static class Roles
public const string ADMIN = "Admin";
public const string VIEWER = "Viewer";
public const string ADMIN_OR_VIEWER = ADMIN + "," + VIEWER;
然后你可以在控制器类或控制器方法(或两者)上使用授权属性:
[Authorize(Roles = Roles.ADMIN]
public class ExampleController : Controller
[Authorize(Roles = Roles.ADMIN_OR_VIEWER)
public ActionResult Create()
..code here...
【讨论】:
这个例子不起作用,或者至少不是你想象的那样。例如,虽然新颖,但动作中的ADMIN_OR_VIEWER
角色是多余的,因为如果您还没有 ADMIN
角色,则不允许您使用 Create
方法。在这种情况下,VIEWER
将永远无法调用 Create
方法。
此解决方案也不可扩展。会有一个点,你有太多不同动作的角色,你不应该创建每个组合
@JohnLeidegren 这是不正确的。操作级别的 AuthorizeAttribute 会覆盖控制器级别的 AuthorizeAttribute,否则,您将无法在具有 AuthorizeAttribute 的控制器中使用 AllowAnonymousAttribute 装饰操作【参考方案5】:
我所做的是@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);
你有它,它现在正在验证角色是否被授权访问资源
【讨论】:
以上是关于授权具有多个角色的属性的主要内容,如果未能解决你的问题,请参考以下文章