最佳实践:自定义asp.net核心身份授权

时间:2018-08-15 21:15:53

标签: asp.net-core asp.net-identity asp.net-core-2.0 claims-based-identity

我对asp.net身份服务器还很陌生。我知道如何自定义IdentityUser实体和脚手架/覆盖Identity UI。考虑到以下情况,我正在寻找有关授权的高级方法和最佳实践:

应用程序中的组件需要考虑以下事项以进行授权:

  • 角色
  • 位置
  • 部门
  • 实体(应用程序中的特定组件。例如“管理新闻”)

从一个类的Web表单背景来看,我倾向于这样定义角色:

public int assignId; //key
public int userId;
public int roleId;
public int? locationId;
public int? divisionId;
public int? entityId;

现实世界中的场景

  • 具有“全局管理员”角色的用户“ Adam”拥有所有权限。
  • 具有“管理员”角色和“ Indy”位置的用户“ Joe”对“ Indy”位置中的每个实体都具有权限。
  • 具有“管理员”角色,位置“ Indy”和部门“ IT”的用户“打击”拥有“ Indy”中每个“ IT”实体的权限
  • 具有“管理员”角色和“芝加哥”角色以及“安全”部门和“新闻”实体的用户“乔”只能为芝加哥的安全部门发布新闻。 (除了以上关于Joe和Indy的规则)

所以,我的问题?

使用asp.net core 2.1身份处理这类授权规则/策略的最佳方法是什么?是否添加“ loc” /“ indy”之类的声明?

我会设置自定义授权处理程序来检查声明吗?还是像在传统的Webforms时代那样,建立一个中间件表来处理关联?还是在这种情况下有最佳实践?

谢谢您的帮助!!!!!!!

1 个答案:

答案 0 :(得分:2)

ASP.NET的早期版本使用基于角色的方法。但是,在这种情况下,首选新的基于声明的方法。因为我们几乎无法确定允许哪个角色访问资源。

假设您描述了上述三个用户并声称:

  • Adam:Role =全局管理员
  • Joe:角色=管理员; Location = Indy&Location = Chicago;部门=安全; Entity = News
  • Blow:管理员;位置=印地;部门= IT;

AspNetUserClaims表将用户的声明记录为bleow:

ID |用户名| ClaimType | ClaimValue |

---- |:------ |:---------- |:----------:|

3 | 3ff3d2db-5a8f-4b01-99b5-fe46d22c240c |角色|全球管理员

4 | cea5d395-fd46-4e6a-aa81-2f4c011b74be |角色|管理员

5 | cea5d395-fd46-4e6a-aa81-2f4c011b74be |位置|印第

6 | cea5d395-fd46-4e6a-aa81-2f4c011b74be |位置|芝加哥

8 | cea5d395-fd46-4e6a-aa81-2f4c011b74be |事业部安全

9 | cea5d395-fd46-4e6a-aa81-2f4c011b74be |实体|新闻

10 | b60c7b75-e31b-4856-ba98-666d013c8201 |角色|管理员

11 | b60c7b75-e31b-4856-ba98-666d013c8201 |位置|印第

15 | b60c7b75-e31b-4856-ba98-666d013c8201 |事业部IT

如您所见,基于声明方法的记录非常简单明了。当需要授权用户时,我们可以将用户的声明与政策进行比较:

services.AddAuthorization(opts=> {
    // ... other policy ...
    // ...
    opts.AddPolicy("Check:Role|Location|Division|Entity", pb=>
        pb.RequireAssertion(async context=> await RldeChecker.Handle(context) )
    );
})

Checker.Handle(context)这里是一个简单的静态方法,该方法接收 AuthorizationHandlerContex 的实例作为参数,并检查用户是否可以访问某些特定资源。 / p>

为了更加清楚,我们可以添加一个 PolicyChecker / 文件夹,并将RldeChecker类放入其中:

public class RldeChecker
{
    // ...

    public static async Task<bool> Handle(AuthorizationHandlerContext context) {

        var user = context.User;
        // bypass all checks
        if (user.HasClaim("Role","Global Admin" )) { return true; }
        try
        {
            // retrieve the user claims
            var userLocation = user.FindFirst("Location")?.Value;
            var userDivision = user.FindFirst("Division")?.Value;
            var userEntity = user.FindFirst("Entity")?.Value;
            // retrieve the resource that the user want to access at runtime
            var resource = (Dictionary<string, string>)context.Resource; 
            var targetLocation = resource["Location"];
            var targetDivision = resource["Division"];
            var targetEntity = resource["Entity"];

            // check for local admin
            // ...
        }
        catch {
            return false;
        }
        return false;
    }
}

当我们要在操作方法中授权用户时,我们可以简单地插入IAuthorizationService的实例以通过authService.Authorize(user,resource,"Check:Role|Location|Division|Entity")进行检查。此外,基于声明的方法允许我们以服务方式使用它,然后我们可以根据需要将其注入到任何地方,例如,根据当前用户的位置/部门/实体显示不同的内容:

var resource = new Dictionary<string, string>() {
    { "Location","Indy"},
    { "Division","IT"},
    { "Entity","News"},
};
var x = await this._authorizationService.AuthorizeAsync(User, resource, "Check:Role|Location|Division|Entity");
if (x.Succeeded)
{
    return View();
}
else
{
    return new ForbidResult();
}