asp.net核心政策和声明可以处理基于资源/活动的授权吗?

时间:2016-07-06 03:41:40

标签: asp.net authorization

我正在研究asp.net核心以及新的安全策略和声明功能。刚刚看过它我看不出它比过去现有的授权属性逻辑更好,在过去硬编码的角色或用户在控制器,方法等上进行装饰。对我来说,问题刚刚从硬 - 将属性编码为硬编码策略。

理想情况下,我想执行基于活动/资源的授权,其中一切都是数据库驱动的。每个活动或资源都将存储在数据库中,并且权限/角色将分配给该资源。

在研究这个话题时,我发现Stefan Wloch写的这篇精彩文章几乎涵盖了我正在寻找的内容。

http://www.codeproject.com/Articles/1079552/Custom-Roles-Based-Access-Control-RBAC-in-ASP-NE

所以我的问题是新的核心功能如何在更改允许访问控制器中的控制器或方法的角色/权限时,如何阻止我们进行硬编码和重新编译?我理解索赔如何用于存储任何东西,但政策部分似乎容易改变,这使我们回到原点。不要误解我的意思,热爱asp.net核心以及所有重大变化,只需寻找有关如何处理授权的更多信息。

1 个答案:

答案 0 :(得分:0)

在实现您想要的东西时至少需要考虑两件事。第一个是如何在数据库中对Controller-Action访问进行建模,第二个是将该设置应用于asp.net核心标识。

第一个,有太多的可能性取决于应用程序本身,因此让我们创建一个名为IActivityAccessService的服务接口进行封装。我们通过依赖项注入来使用该服务,以便可以将我们需要的任何内容注入其中。

对于第二个,可以通过在基于策略的授权中自定义AuthorizationHandler来实现。第一步是在Startup.ConfigureServices中进行设置:

services.AddAuthorization(options =>
{
    options.AddPolicy("ActivityAccess", policy => policy.Requirements.Add( new ActivityAccessRequirement() ));
});
services.AddScoped<IAuthorizationHandler, ActivityAccessHandler>();
//inject the service also
services.AddScoped<IActivityAccessService, ActivityAccessService>();
//code below will be explained later
services.AddHttpContextAccessor();

接下来,我们创建ActivityAccessHandler

public class ActivityAccessHandler : AuthorizationHandler<ActivityAccessRequirement>
{
    readonly IActivityAccessService _ActivityAccessService;

    public ActivityAccessHandler (IActivityAccessService r)
    {
        _ActivityAccessService = r;
    }

    protected override async Task HandleRequirementAsync(AuthorizationHandlerContext authHandlerContext, ActivityAccessRequirement requirement)
    {
        if (context.Resource is AuthorizationFilterContext filterContext)
        {
            var area = (filterContext.RouteData.Values["area"] as string)?.ToLower();
            var controller = (filterContext.RouteData.Values["controller"] as string)?.ToLower();
            var action = (filterContext.RouteData.Values["action"] as string)?.ToLower();
            var id = (filterContext.RouteData.Values["id"] as string)?.ToLower();
            if (_ActivityAccessService.IsAuthorize(area, controller, action, id))
            {
                context.Succeed(requirement);
            }               
        }            
    }
}

public class ActivityAccessRequirement : IAuthorizationRequirement 
{
    //since we handle the authorization in our service, we can leave this empty
}

由于我们可以在AuthorizationHandler中使用依赖项注入,因此我们在这里注入了IActivityAccessService

现在我们可以访问所请求的资源,我们需要知道谁在请求资源。这可以通过注入IHttpContextAccessor来完成。因此,services.AddHttpContextAccessor()被添加到上面的代码中,正是由于这个原因。

对于IActivityAccessService,您可以执行以下操作:

public class ActivityAccessService : IActivityAccessService
{
    readonly AppDbContext _context;
    readonly IConfiguration _config;
    readonly IHttpContextAccessor _accessor;
    readonly UserManager<AppUser> _userManager;

    public class ActivityAccessService(AppDbContext d, IConfiguration c, IHttpContextAccessor a, UserManager<AppUser> u) 
    {
        _context = d;
        _config = c;
        _accessor = a;
        _userManager = u;
    }

    public bool IsAuthorize(string area, string controller, string action, string id)
    {
        //get the user object from the ClaimPrincipals       
        var appUser = await _userManager.GetUserAsync(_accessor.HttpContext.User);
        //get user roles if necessary
        var userRoles = await _userManager.GetRolesAsync(appUser);
        // all of needed data are available now, do the logic of authorization
        return result;
    } 
}

请注意,上面IsAuthorize主体中的代码是一个示例。虽然它会起作用,但人们可能会说这不是一个好习惯。但是由于IActivityAccessService只是一个普通的简单服务类,因此我们可以注入所需的任何内容,并以我们想要的任何方式修改IsAuthorize方法签名。例如,我们可以只传递filterContext.RouteData

关于如何将其应用于控制器或动作:

[Authorize(Policy = "ActivityAccess")] 
public ActionResult<IActionResult> GetResource(int resourceId)
{
    return Resource;
}

希望这会有所帮助