在Web API(.Net Framework)中创建自定义AuthorizeAttribute

11

我在我的WebAPI中使用OAuth2.0 Owin(密码授权)。我的初始令牌响应如下所示:

{
    "access_token": "_ramSlQYasdsRTWEWew.....................",
    "token_type": "bearer",
    "expires_in": 17999,
    "permissions": {
        "user": [
            "Add",
            "Update",
            "Delete"
        ],
        "Product": [
            "Read",
            "Create"
        ]
    }
}

我已经通过创建一个名为permissions的新键来自定义响应,它保存了相应用户的特权。
从这里开始,我需要通过使用Authorize属性检查用户是否有足够的权限调用API,以此来验证每个来自我的资源服务器的请求。
我在这里找到了一个类似的例子,但它是针对Dot net Core的,不适用于我的情况。
困难的部分是permission JSON Key本身包含一个与ArrayList相关的复杂性。
[CustomAuthorize(PermissionItem.Product, PermissionAction.Read)]
    public async Task<IActionResult> Index()
    {
        return View(Index);
    }

public class CustomAuthorize : AuthorizeAttribute {
    public AuthorizeAttribute (PermissionItem item, PermissionAction action) {
        //Need to initalize the Permission Enums
    }
    public override void OnAuthorization (HttpActionContext actionContext) {
        //Code to get the value from Permissions ArrayList and compare it with the Enum values
    }
}

上述是我的想法。但由于Permissions密钥和枚举比较的复杂性,我无法继续前进。
此外,还有一个问题,如果用户的权限是添加和更新,则需要在我的控制器之前创建两个属性条件。
[CustomAuthorize(PermissionItem.User, PermissionAction.Add)]
[CustomAuthorize(PermissionItem.User, PermissionAction.Update)]

这导致添加更多的属性行。那么有没有办法将其作为单个条件,用|分隔?
[CustomAuthorize(PermissionItem.User, PermissionAction.Update|PermissionAction.Add)]
3个回答

9
为什么不允许您的CustomAuthorize构造函数具有多个Permission操作。
public class CustomAuthorize : AuthorizeAttribute
{
    private readonly PermissionAction[] permissionActions;

    public CustomAuthorize(PermissionItem item, params PermissionAction[] permissionActions)
    {
        this.permissionActions = permissionActions;
    }

    public override void OnAuthorization(HttpActionContext actionContext)
    {
        var currentIdentity = System.Threading.Thread.CurrentPrincipal.Identity;
        if (!currentIdentity.IsAuthenticated) {
            // redirect to access denied page
        }

        var userName = currentIdentity.Name;
        // step 1 : retrieve user object

        // step 2 : retrieve user permissions

        // step 3 : match user permission(s) agains class/method's required premissions

        // step 4 : continue/redirect to access denied page
    }
}

你需要在类上注释: [CustomAuthorize(PermissionItem.User, PermissionAction.Update, PermissionAction.Add)] 我不确定OP想要实现什么。如果你依赖HTTP请求提供访问权限,那么会存在很大的安全漏洞。每次请求时,你应该从数据库中检索用户的访问权限信息,然后将其与类/方法所需的权限进行匹配。
经验法则是不要依赖请求对象告诉你当前用户拥有哪些权限。你应该从数据存储中检索它们。 我的CustomAttribute实现
public class CustomAuthorize : System.Web.Http.AuthorizeAttribute
{
    private readonly PermissionAction[] permissionActions;

    public CustomAuthorize(PermissionItem item, params PermissionAction[] permissionActions)
    {
        this.permissionActions = permissionActions;
    }

    protected override Boolean IsAuthorized(HttpActionContext actionContext)
    {
        var currentIdentity = actionContext.RequestContext.Principal.Identity;
        if (!currentIdentity.IsAuthenticated)
            return false;

        var userName = currentIdentity.Name;
        using (var context = new DataContext())
        {
            var userStore = new UserStore<AppUser>(context);
            var userManager = new UserManager<AppUser>(userStore);
            var user = userManager.FindByName(userName);

            if (user == null)
                return false;

            foreach (var role in permissionActions)
                if (!userManager.IsInRole(user.Id, Convert.ToString(role)))
                    return false;

            return true;
        }
    }
}

我需要实际的逻辑部分来比较权限枚举和权限数组列表。 - Jayendran
我只从数据库中获取用户的访问权限。您能详细解释一下第三步吗?我需要实际的逻辑。 - Jayendran

2
我们已经为身份验证创建了以下API过滤器。
在这里,“SecretToken”和“MerchantKey”这两个密钥通过API请求进行传递。我们使用“IsValidMerchant”函数从数据库中验证这两个密钥。
“IsValidMerchant”函数直接连接到存储相应值的数据库表。
public void OnAuthorization(AuthorizationFilterContext actionContext)
{
  const string secretTokenName = "SecretToken";
  const string merchentKeyName = "MerchantKey";
  bool isValid = false;

  if (!actionContext.Filters.Any(item => item is IAllowAnonymousFilter))
    {
     CPServiceResponse response = new CPServiceResponse();
     var secretToken = actionContext.HttpContext.Request.Headers[secretTokenName].FirstOrDefault();
     var merchentKey = actionContext.HttpContext.Request.Headers[merchentKeyName].FirstOrDefault();

      isValid = this.IsValidMerchant(merchentKey, secretToken,_productCode);

       if (isValid == false)
        {
          response.Status = (int)HttpStatusCode.Unauthorized;
          response.Message = Hegic.Shared.Resource.Common.UnauthorizedRequestError;
          actionContext.Result = new JsonResult("")
                {
                    Value = new { Status = response }
                };
         }
      }
  }

1
你可以使用标志和二进制操作,使不同的操作进行|运算。
以下代码展示了一个小例子。
class Program {
    static void Main(string[] args) {
        Test test = new Test();
        CustomAuthorizeAttribute customAuthorizeAttribute = (CustomAuthorizeAttribute)Attribute.GetCustomAttribute(typeof(Test), typeof(CustomAuthorizeAttribute));

        customAuthorizeAttribute.Test();

        Console.ReadKey();
    }
}

[CustomAuthorize(PermissionActions = PermissionAction.Add | PermissionAction.Delete)]
public class Test {

}

public class CustomAuthorizeAttribute : Attribute {
    public PermissionAction PermissionActions { get; set; }

    public void Test() {
        if ((PermissionActions & PermissionAction.Add) == PermissionAction.Add) Console.WriteLine("Add");
        if ((PermissionActions & PermissionAction.Delete) == PermissionAction.Delete) Console.WriteLine("Delete");
        if ((PermissionActions & PermissionAction.Update) == PermissionAction.Update) Console.WriteLine("Update");
    }
}

public enum PermissionAction {
    Add = 1,
    Update = 2,
    Delete = 4
}

产生以下输出

enter image description here


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