ASP.NET Core中的基于活动的授权

我们有一个asp.net mvc应用程序,我正在移植到aspnet核心mvc。 在旧的解决方案中使用Windows身份验证完成身份验证。

最重要的是,我们有一个“基于活动的认证”(如http://ryankirkman.com/2013/01/31/activity-based-authorization.html); 用户连接到角色并且角色连接到权限。 用户角色和相应的权限存储在一个单独的应用程序中,作为我们的应用程序和少数其他系统的授权服务。

对授权服务api查询用户“Jon Doe”权限的查询将得到如下回复:

{
    Email:"Jon.Doe@acme.com",
    FirstName:"Jon",
    LastName:"Doe",
    Resources:
    [
        "CanAccessWebApplication",
        "CanCopyAppointment",
        "CanEditAppointment",
        "CanEditContact",
        "CanSaveContact"
        ...
    ]
    Alias:"1234567",
    UserId:"1234"
}

在我们当前的应用程序中,这些权限是通过控制器方法使用属性(我们已经实现了自己)来检查的:

public ContactController
{
     [ActionUserAccess("CanSaveContact")]
     public ActionResult SaveContact
     {
       ...
     }
}

ActionUserAccessAttribute过滤器的当前传统实现如下所示:

    [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class)]
    public sealed class ActionUserAccessAttribute : ActionFilterAttribute
    {
        private readonly string _accessRight;

        public ActionUserAccessAttribute(string accessRight)
        {
            _accessRight = accessRight;
        }

        public override void OnActionExecuting(ActionExecutingContext filterContext)
        {
            if (!filterContext.HttpContext.User.Identity.IsAuthenticated)
            {
                throw new InvalidOperationException("ActionUserAccessAttribute can not be used for controllers or actions configured for anonymous access");
            }

            base.OnActionExecuting(filterContext);

            var securityService = ContainerResolver.Container.GetInstance<ISecurityService>();
            var hasResource = securityService.HasAccess(_accessRight);

            if (!hasResource)
            {
                filterContext.Result =
                    new HttpStatusCodeResult(
                        403,
                        string.Format(
                            "User {0} is not authorized to access the resource:'{1}' ",
                            filterContext.HttpContext.User.Identity.Name,
                            _accessRight));
            }
        }
    }
}

将属性/过滤器移植到aspnetcore似乎很简单,但根据这个答案https://stackoverflow.com/a/31465227/1257728通过“asp.net安全人员”@blowdart我们不应该。

如果不将自定义过滤器移植到aspnetcore,那么最适合在这里实现的是什么? 也许我们可以使用基于角色的身份验证https://docs.microsoft.com/en-us/aspnet/core/security/authorization/roles? 我们可以创建一个中间件,从授权服务api中填充用户访问权限,并将这些权限展平并将它们作为ClaimTypes.Role添加到用户的ClaimsIdentity ? 然后我们将使用上面的方法,如:

[Authorize(Roles = "CanSaveContact")]
public ActionResult Save()

这种方法的不合适之处在于,这不是关于角色,而是关于访问权限。

我也看了一下基于策略的授权:

https://docs.microsoft.com/en-us/aspnet/core/security/authorization/policies

在控制器中可能看起来像这样:

[Authorize(Policy = "CanSaveContact")]
public ActionResult Save()

但是,当我阅读上面基于Microsoft的基于策略的示例中的代码时,我将不得不将安全服务api中存在的所有可用访问权限添加为Startup类的ConfigureService方法中的策略,以便能够使用它们。 我认为似乎很尴尬(伪代码):

public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc();
    IEnumerable<string> allAccessRights = _securtiyService.GetAllAccessRights();

    services.AddAuthorization(options =>
    {
        foreach(var accessRight in allAccessRights)
        {
            options.AddPolicy(accessRight, policy => policy.Requirements.Add(new AccessRightRequirement(accessRight));

        }
    });
    services.AddSingleton<IAuthorizationHandler, AccessRightHandler>();
}

AccessRightHandler然后将被AccessRightHandler以验证用户的访问权限。 编写一个AccessRightHandler是可以的,但似乎没有必要将所有权限添加为策略。

在我们的aspnetcore应用程序中实现这种授权的最佳方法是什么?


很好的问题,我认为许多人升级到ASP.NET Core也会遇到同样的问题。

Barry Dorrans(@blowdart)是绝对正确的,你不应该编写你自己的自定义授权属性 - ASP.NET Core中的授权已经得到了很大的改进,你可以根据自己的需求进行模拟。

这当然很大程度上取决于你当前的应用程序,你有什么样的角色,所以我会根据你上面提供的代码片断做出一些假设。

在我开始之前,我真的建议您阅读ASP.NET Core的新授权文档以及GitHub上的Barry Dorran的授权研讨会。 我强烈建议你通过后者,他还有一个.NET Core 2.0分支。

根据你想要实现它的方式,你可以使用基于声明的授权,或者去基于资源。

看看你的角色,似乎基于资源的认证可以在你的情况下真正起作用!

例如:

确定可能的操作(操作Name将从您的资源中获取):

public static class Operations
{
    public static OperationAuthorizationRequirement Access = new OperationAuthorizationRequirement { Name = "Access" };
    public static OperationAuthorizationRequirement Copy = new OperationAuthorizationRequirement { Name = "Copy" };
    public static OperationAuthorizationRequirement Edit = new OperationAuthorizationRequirement { Name = "Edit" };
    public static OperationAuthorizationRequirement Save = new OperationAuthorizationRequirement { Name = "Save" };
    public static OperationAuthorizationRequirement Delete = new OperationAuthorizationRequirement { Name = "Delete" };
}

创建基础资源授权处理程序:

public abstract class BaseResourceAuthorizationHandler<TResource> : AuthorizationHandler<OperationAuthorizationRequirement, TResource>
{
    private readonly string _resourceType;
    public BaseResourceAuthorizationHandler(string resourceType)
    {
        _resourceType = resourceType;
    }

    protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, OperationAuthorizationRequirement requirement, TResource resource)
    {
        if (context.User.HasClaim("Resources", $"Can{requirement.Name}{_resourceType}"))
        {
            context.Succeed(requirement);
        }

        return Task.CompletedTask;
    }
}

实现特定的资源处理程序。 这些资源是您的应用程序中的资源对象中的绑定对象。 这个类将成为当前资源角色,操作和ASP.NET Core授权系统之间的粘合剂。 这些还可以扩展为为任何特定的资源类型/操作添加额外的逻辑
例如,对于约会:

public class AppointmentAuthorizationHandler : BaseResourceAuthorizationHandler<Appointment>
{
    public AppointmentAuthorizationHandler() : base("Appointment") { }
}

你然后注册:

services.AddSingleton<IAuthorizationHandler, AppointmentAuthorizationHandler>();

然后在你的控制器中:

public class AppointmentsController : Controller
{
    IAppointmentsRepository _appointmentsRepository;
    IAuthorizationService _authorizationService;

    public AppointmentsController(IAppointmentsRepository appointmentsRepository,
                              IAuthorizationService authorizationService)
    {
        _appointmentsRepository = appointmentsRepository;
        _authorizationService = authorizationService;
    }

    public IActionResult Edit(int id)
    {
        var appointment = _appointmentsRepository.Get(id);
        if (appointment == null)
        {
            return new NotFoundResult();
        }

        if (!(await _authorizationService.AuthorizeAsync(User, appointment, Operations.Edit)))
        {
            return new ChallengeResult();
        }

        return View(appointment);
    }
}

您也可以在视图中执行相同操作,以检查是否允许用户看到“编辑”按钮,例如:

@using Microsoft.AspNetCore.Authorization
@model IEnumerable<Appointment>
@inject IAuthorizationService AuthorizationService
<h1>Document Library</h1>
@foreach (var appointment in Model)
{
    if (await AuthorizationService.AuthorizeAsync(User, appointment, Operations.Edit))
    {
        <p>@Html.ActionLink("Appointment #" + appointment.Id, "Edit", new { id = appointment.Id })</p>
    }
}

PS只是添加一个注释 - 是的,你失去了按属性过滤的能力,但最终这种方式更好。 首先也是最重要的 - 您从基于字符串的角色移开,您根据操作类型和资源类型请求权限。 其次,您可以更好地(以及智能的方式)处理权限,并组合多个权限检查。

它看起来更复杂,但它也更强大:)


在这里扮演魔鬼的拥护者,并建议替代我的其他答案 - 这可能是基于@ mortb的请求的一个更简单的选项,并且可能适合正在从当前系统迁移的一些人。

根据您的情况,基于策略的身份验证确实不适合您的用例 - 这是一个更强大的选项,除了检查API中是否存在Resource字符串外,您没有真正使用它。

另一方面,我不会放弃角色方法。 您从外部API获取的资源列表并不完全是资源,但同时它完全符合您的需求。 在一天结束时,您所要做的只是检查用户是否具有针对特定请求的一个(或多个)资源访问权限。

就像您在帖子中提到的那样,您必须扩展授权才能从外部API填充角色。 不要忘记,您的ClaimsIdentity具有RoleClaimType属性,该属性标记用于存储角色的声明的类型。 它通常会被设置为ClaimTypes.Role ,但并非总是如此。

你甚至可以继续,并创建自定义auth属性,与此不同:

public class AuthorizeAccessAttribute : AuthorizeAttribute
{
    public AuthorizeAccessAttribute(string entity)
    {
        Roles = "CanAccess" + entity;
    }
}
public class AuthorizeEditAttribute : AuthorizeAttribute
{
    public AuthorizeEditAttribute(string entity)
    {
        Roles = "CanEdit" + entity;
    }
}

所以你可以使用它如下:

[AuthorizeEdit("Appointment")]
public IActionResult Edit(int id)
{
    return View();
}
链接地址: http://www.djcxy.com/p/15971.html

上一篇: Activity based Authorization in ASP.NET Core

下一篇: based authorization in ASP.NET MVC3