不能在内部包含动态代码的[Authorize()]

时间:2019-04-17 12:06:45

标签: c# asp.net asp.net-core asp.net-core-mvc

我正在尝试使授权接受为枚举或smart enum角色 这样我就不必调试魔术字符串及其错别字

但是我总是遇到以下两个错误:

  
      
  • 属性构造函数参数“角色”具有类型“角色[]”,这不是有效的属性参数类型

  •   
  • 属性参数必须是属性参数类型的常量表达式,typeof表达式或数组创建表达式

  •   

这是我的代码:

AuthorizeRoles.cs

public class AuthorizeRoles : AuthorizeAttribute
{
    public AuthorizeRoles(params Role[] roles)
    {
        string allowed = string.Join(", ", roles.ToList().Select(x => x.Name));
        Roles = allowed;
    }
}

Role.cs

public class Role
{
    public readonly string Name;

    public enum MyEnum  // added
    {
        Admin,
        Manager
    }

    public static readonly Role Admin = new Role("Admin");
    public static readonly Role Manager = new Role("Manager");

    public Role(string name)
    {
        Name = name;
    }

    public override string ToString()
    {
        return Name;
    }

在控制器内部,我做到了

    [AuthorizeRoles(Role.Admin, Role.Manager)]
    [AuthorizeRoles(Role.MyEnum.Admin)] // added 
    public IActionResult Index()
    {
        return Content("hello world");
    }

我已经看过这些答案,但是没有用

4 个答案:

答案 0 :(得分:3)

由于CLR约束(属性如何存储在元数据中),属性参数只能是原始类型或这些类型(和Type)的数组。您不能Role(自定义对象)传递给属性。

枚举有效,但是编译器无法将您的枚举(Role.MyEnum)转换为Role的类型,即AuthorizeRoles。所以这是编译器错误。

您可以猜到,解决方案是创建一个采用Role.MyEnum数组的构造函数,如下所示:

public class AuthorizeRoles : Attribute
{
    public string Roles { get; private set; }

    public AuthorizeRoles(params Role.MyEnum[] roles)
    {
        string allowed = string.Join(", ", roles);
        Roles = allowed;
    }
}

public class Role
{
    public readonly string Name;

    public enum MyEnum
    {
        Admin,
        Manager
    }

    public Role(string name)
    {
        Name = name;
    }

    public override string ToString()
    {
        return Name;
    }
}

// ...

[AuthorizeRoles(Role.MyEnum.Admin)]
public IActionResult Index()
{
    // ...
}

答案 1 :(得分:2)

在构造函数AuthorizeRoles类中,您使用Role类的数组,但在属性[AuthorizeRoles(Role.MyEnum.Admin)]中,您使用类型为MyEnum的参数。如果要使用枚举,则必须使用MyEnum类型的参数创建AuthorizeRoles类构造函数。

答案 2 :(得分:1)

它确实很烂,但实际上您可以最接近它的方法是:

public static class Roles
{
    public const string Admin = "Admin";
    public const string Manager = "Manager";
}

然后:

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

在常量字符串组合和就地字符串连接之间,它们仍然是“常量表达式”。基本上,您无法做的事情是需要运行诸如string.Join之类的方法。这就是使用属性时的游戏中断。

答案 3 :(得分:0)

使用常量作为策略名称并使用授权策略

// startup.cs

services.AddAuthorization(options =>
        {
            options.AddPolicy(PolicyConstants.Admin, policy =>
            {
                // Allowed to access the resource if role admin or manager
                policy.RequireClaim(JwtClaimTypes.Role, new[] { PolicyConstants.Admin, PolicyConstants.Manager });

               // Or use LINQ here
                policy.RequireAssertion(c =>
                {

                    // c.User.Claims
                });
}

在控制器中使用策略名称

[Authorize(PolicyConstants.Admin)]
public class TestController
{
    // here also you can use specific policy and for controller, you can use other policy. It will match Action Level policy first and then match controller policy.
    public IActionResult Index()
    {
    }
}