it-swarm.com.de

Implementieren der zulassungsbasierten Zugriffssteuerung mit Asp.Net Core

Ich versuche, eine auf Berechtigungen basierende Zugriffssteuerung mit Aspnet Core zu implementieren. Zum dynamischen Verwalten von Benutzerrollen und Berechtigungen (create_product, delete_product usw.) werden sie in der Datenbank gespeichert. Datenmodell ist wie http://i.stack.imgur.com/CHMPE.png

Vor dem Aspnet-Kern (in MVC 5) verwendete ich benutzerdefinierte AuthorizeAttribute wie unten, um das Problem zu behandeln:

public class CustomAuthorizeAttribute : AuthorizeAttribute
{
    private readonly string _permissionName { get; set; }
    [Inject]
    public IAccessControlService _accessControlService { get; set; }

    public CustomAuthorizeAttribute(string permissionName = "")
    {
        _permissionName = permissionName;
    }

    public override void OnAuthorization(AuthorizationContext filterContext)
    {
        base.OnAuthorization(filterContext);
        var user = _accessControlService.GetUser();
        if (PermissionName != "" && !user.HasPermission(_permissionName))
        {
            // set error result
            filterContext.HttpContext.Response.StatusCode = 403;
            return;
        }
        filterContext.HttpContext.Items["CUSTOM_USER"] = user;
    }
}

Dann habe ich es in der Aktionsmethode wie folgt verwendet:

[HttpGet]
[CustomAuthorize(PermissionEnum.PERSON_LIST)]
public ActionResult Index(PersonListQuery query){ }

Darüber hinaus habe ich HttpContext.Items ["CUSTOM_USER"] in Ansichten verwendet, um den HTML-Teil anzuzeigen oder auszublenden:

@if (CurrentUser.HasPermission("<Permission Name>"))
{

}

Als ich mich entschied, den Aspnet-Kern zu wechseln, war mein Plan gescheitert. Weil es keine virtuelle OnAuthorization-Methode in der AuthorizeAttribute gab. Ich habe versucht, ein Problem zu lösen. Das sind unten:

  • Neue richtlinienbasierte Autorisierung verwenden (ich denke, es ist nicht geeignet für ___meiner Bereich)

  • Benutzerdefinierte AuthorizeAttribute und AuthorizationFilter verwenden (ich habe dies gelesenpost https://stackoverflow.com/a/35863514/5426333 , aber ich konnte es nicht richtig ändern.)

  • Benutzerdefinierte Middleware verwenden (wie erhält man AuthorizeAttribute von der aktuellen Aktion?)

  • ActionFilter verwenden (ist dies aus Sicherheitsgründen korrekt?)

Ich konnte mich nicht entscheiden, welcher Weg der beste für mein Szenario ist und wie er umgesetzt werden soll.

Erste Frage: Ist die Implementierung von MVC5 eine schlechte Praxis?

Zweite Frage: Haben Sie Vorschläge, Aspnet Core zu implementieren?

30
adem caglin

Anhand der Kommentare ein Beispiel zur Verwendung der richtlinienbasierten Autorisierung: 

public class PermissionRequirement : IAuthorizationRequirement
{
    public PermissionRequirement(PermissionEnum permission)
    {
         Permission = permission;
    }

    public PermissionEnum Permission { get; }
}

public class PermissionHandler : AuthorizationHandler<PermissionRequirement>
{
    private readonly IUserPermissionsRepository permissionRepository;

    public PermissionHandler(IUserPermissionsRepository permissionRepository)
    {
        if(permissionRepository == null)
            throw new ArgumentNullException(nameof(permissionRepository));

        this.permissionRepository = permissionRepository;
    }

    protected override void Handle(AuthorizationContext context, PermissionRequirement requirement)
    {
        if(context.User == null)
        {
            // no user authorizedd. Alternatively call context.Fail() to ensure a failure 
            // as another handler for this requirement may succeed
            return null;
        }

        bool hasPermission = permissionRepository.CheckPermissionForUser(context.User, requirement.Permission);
        if (hasPermission)
        {
            context.Succeed(requirement);
        }
    }
}

Und registrieren Sie es in Ihrer Startup-Klasse: 

services.AddAuthorization(options =>
{
    UserDbContext context = ...;
    foreach(var permission in context.Permissions) 
    {
        // assuming .Permission is enum
        options.AddPolicy(permission.Permission.ToString(),
            policy => policy.Requirements.Add(new PermissionRequirement(permission.Permission)));
    }
});

// Register it as scope, because it uses Repository that probably uses dbcontext
services.AddScope<IAuthorizationHandler, PermissionHandler>();

Und schließlich im Controller

[HttpGet]
[Authorize(Policy = PermissionEnum.PERSON_LIST.ToString())]
public ActionResult Index(PersonListQuery query)
{
    ...
}

Der Vorteil dieser Lösung besteht darin, dass Sie auch mehrere Handler für eine Anforderung haben können, d. H. Wenn der erste Handler erfolgreich ist, kann der zweite Handler feststellen, dass ein Fehler aufgetreten ist, und Sie können ihn mit Ressourcen-basierter Autorisierung mit geringem zusätzlichen Aufwand verwenden.

Der auf Richtlinien basierende Ansatz wird vom ASP.NET-Core-Team bevorzugt. 

Von blowdart :

Wir möchten nicht, dass Sie benutzerdefinierte Berechtigungsattribute schreiben. Wenn Sie das tun müssen, haben wir etwas falsch gemacht. Stattdessen sollten Sie Autorisierungsanforderungen schreiben.

43
Tseng

Für eine Lösung, für die Sie keine Richtlinie für jede Berechtigung hinzufügen müssen, finden Sie weitere Informationen in meinem answer .

Damit können Sie Ihre Controller und Aktionen mit beliebigen benutzerdefinierten Attributen versehen und in Ihrem AuthorizationHandler darauf zugreifen.

1
Shawn

Ich hatte die gleiche Anforderung und habe es wie unten gemacht und es funktioniert gut für mich. Ich verwende .Net Core 2.0 Webapi

         [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true, Inherited = true)]
public class CheckAccessAttribute : AuthorizeAttribute, IAuthorizationFilter
{
    private string[] _permission;
    public CheckAccessAttribute(params string[] permission)
    {
        _permission = permission;
    }

    public void OnAuthorization(AuthorizationFilterContext context)
    {
        var user = context.HttpContext.User;

        if (!user.Identity.IsAuthenticated)
        {
            return;
        }

        IRepository service = (IRepositoryWrapper)context.HttpContext.RequestServices.GetService(typeof(IRepository));
          var success = service.CheckAccess(userName, _permission.ToList());
            if (!success)
            {
                context.Result = JsonFormatter.GetErrorJsonObject(CommonResource.error_unauthorized,
                        StatusCodeEnum.Forbidden);
                return;
            }
        return;
    }
}

Im Controller verwenden Sie es wie folgt 

        [HttpPost]
    [CheckAccess(Permission.CreateGroup)]
    public JsonResult POST([FromBody]Group group)
    {
       // your code api code here.
    }
1
vinayak shettar