c# - authorizeattribute asp net core




Come si crea un attributo Authorize personalizzato in ASP.NET Core? (5)

Sto cercando di creare un attributo di autorizzazione personalizzato in ASP.NET Core. Nelle versioni precedenti era possibile sovrascrivere bool AuthorizeCore(HttpContextBase httpContext) . Ma questo non esiste più in AuthorizeAttribute .

Qual è l'approccio attuale per realizzare un AuthorizeAttribute personalizzato?

Cosa sto cercando di ottenere: sto ricevendo un ID sessione nell'autorizzazione dell'intestazione. Da quell'ID saprò se una determinata azione è valida.


Qual è l'approccio attuale per realizzare un AuthorizeAttribute personalizzato

Semplice: non creare il proprio AuthorizeAttribute .

Per scenari di autorizzazione puri (come limitare l'accesso solo a utenti specifici), l'approccio consigliato è utilizzare il nuovo blocco di autorizzazione: https://github.com/aspnet/MusicStore/blob/1c0aeb08bb1ebd846726232226279bbe001782e1/samples/MusicStore/Startup.cs#L84-L92

public enum PermissionItem
{
    User,
    Product,
    Contact,
    Review,
    Client
}

public enum PermissionAction
{
    Read,
    Create,
}


public class AuthorizeAttribute : TypeFilterAttribute
{
    public AuthorizeAttribute(PermissionItem item, PermissionAction action)
    : base(typeof(AuthorizeActionFilter))
    {
        Arguments = new object[] { item, action };
    }
}

public class AuthorizeActionFilter : IAuthorizationFilter
{
    private readonly PermissionItem _item;
    private readonly PermissionAction _action;
    public AuthorizeActionFilter(PermissionItem item, PermissionAction action)
    {
        _item = item;
        _action = action;
    }
    public void OnAuthorization(AuthorizationFilterContext context)
    {
        bool isAuthorized = MumboJumboFunction(context.HttpContext.User, _item, _action); // :)

        if (!isAuthorized)
        {
            context.Result = new ForbidResult();
        }
    }
}

public class UserController : BaseController
{
    private readonly DbContext _context;

    public UserController( DbContext context) :
        base()
    {
        _logger = logger;
    }

    [Authorize(PermissionItem.User, PermissionAction.Read)]
    public async Task<IActionResult> Index()
    {
        return View(await _context.User.ToListAsync());
    }
}

Per l'autenticazione, è gestita al meglio a livello di middleware.

Cosa stai cercando di ottenere esattamente?


È possibile creare il proprio AuthorizationHandler che troverà attributi personalizzati sui controller e sulle azioni e li passerà al metodo HandleRequirementAsync.

public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        services.Configure<AuthorizationOptions>(options =>
        {
            options.AddPolicy("ManageStore", policy => policy.RequireClaim("Action", "ManageStore"));
        });
    }
}

public class StoreController : Controller
{
    [Authorize(Policy = "ManageStore"), HttpGet]
    public async Task<IActionResult> Manage() { ... }
}

Quindi puoi usarlo per qualsiasi attributo personalizzato di cui hai bisogno sui tuoi controller o azioni. Ad esempio per aggiungere requisiti di autorizzazione. Basta creare il tuo attributo personalizzato.

public abstract class AttributeAuthorizationHandler<TRequirement, TAttribute> : AuthorizationHandler<TRequirement> where TRequirement : IAuthorizationRequirement where TAttribute : Attribute
{
    protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, TRequirement requirement)
    {
        var attributes = new List<TAttribute>();

        var action = (context.Resource as AuthorizationFilterContext)?.ActionDescriptor as ControllerActionDescriptor;
        if (action != null)
        {
            attributes.AddRange(GetAttributes(action.ControllerTypeInfo.UnderlyingSystemType));
            attributes.AddRange(GetAttributes(action.MethodInfo));
        }

        return HandleRequirementAsync(context, requirement, attributes);
    }

    protected abstract Task HandleRequirementAsync(AuthorizationHandlerContext context, TRequirement requirement, IEnumerable<TAttribute> attributes);

    private static IEnumerable<TAttribute> GetAttributes(MemberInfo memberInfo)
    {
        return memberInfo.GetCustomAttributes(typeof(TAttribute), false).Cast<TAttribute>();
    }
}

Quindi crea un Requisito da aggiungere alla tua Politica

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true)]
public class PermissionAttribute : AuthorizeAttribute
{
    public string Name { get; }

    public PermissionAttribute(string name) : base("Permission")
    {
        Name = name;
    }
}

Quindi crea AuthorizationHandler per il tuo attributo personalizzato, ereditando AttributeAuthorizationHandler che abbiamo creato in precedenza. Verrà passato un IEnumerable per tutti gli attributi personalizzati nel metodo HandleRequirementsAsync, accumulati dal controller e dall'azione.

public class PermissionAuthorizationRequirement : IAuthorizationRequirement
{
    //Add any custom requirement properties if you have them
}

E infine, nel tuo metodo ConfigureServices Startup.cs, aggiungi il tuo AuthorizationHandler personalizzato ai servizi e aggiungi la tua Politica.

public class PermissionAuthorizationHandler : AttributeAuthorizationHandler<PermissionAuthorizationRequirement, PermissionAttribute>
{
    protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, PermissionAuthorizationRequirement requirement, IEnumerable<PermissionAttribute> attributes)
    {
        foreach (var permissionAttribute in attributes)
        {
            if (!await AuthorizeAsync(context.User, permissionAttribute.Name))
            {
                return;
            }
        }

        context.Succeed(requirement);
    }

    private Task<bool> AuthorizeAsync(ClaimsPrincipal user, string permission)
    {
        //Implement your custom user permission logic here
    }
}

Ora puoi semplicemente decorare i tuoi controller e le tue azioni con il tuo attributo personalizzato.

        services.AddSingleton<IAuthorizationHandler, PermissionAuthorizationHandler>();

        services.AddAuthorization(options =>
        {
            options.AddPolicy("Permission", policyBuilder =>
            {
                policyBuilder.Requirements.Add(new PermissionAuthorizationRequirement());
            });
        });

L'approccio raccomandato dal team ASP.Net Core consiste nell'utilizzare il nuovo modello di politica che è completamente documentato here . L'idea alla base del nuovo approccio è quella di utilizzare il nuovo attributo [Autorizza] per designare una "politica" (es. [Authorize( Policy = "YouNeedToBe18ToDoThis")] dove la politica è registrata nel Startup.cs dell'applicazione per eseguire un blocco di codice (vale a dire assicurarsi che l'utente abbia una domanda di età in cui l'età ha 18 anni o più).

Il design della politica è una grande aggiunta al framework e il team ASP.Net Security Core dovrebbe essere lodato per la sua introduzione. Detto questo, non è adatto a tutti i casi. Il difetto di questo approccio è che non riesce a fornire una soluzione conveniente per la necessità più comune di affermare semplicemente che un determinato controller o azione richiede un determinato tipo di reclamo. Nel caso in cui un'applicazione possa disporre di centinaia di autorizzazioni discrete che regolano le operazioni CRUD su singole risorse REST ("CanCreateOrder", "CanReadOrder", "CanUpdateOrder", "CanDeleteOrder", ecc.), Il nuovo approccio richiede uno o l'altro ripetitivi una mappatura tra un nome di politica e un nome di rivendicazione (ad es options.AddPolicy("CanUpdateOrder", policy => policy.RequireClaim(MyClaimTypes.Permission, "CanUpdateOrder)); ) o scrivere un codice per eseguire queste registrazioni in fase di esecuzione ( ad esempio, leggere tutti i tipi di reclami da un database ed eseguire la chiamata di cui sopra in un ciclo.) Il problema con questo approccio per la maggior parte dei casi è che non è necessario un sovraccarico.

Mentre il team ASP.Net Core Security consiglia di non creare mai la propria soluzione, in alcuni casi questa potrebbe essere l'opzione più prudente con cui iniziare.

Di seguito è un'implementazione che utilizza IAuthorizationFilter per fornire un modo semplice per esprimere un requisito di reclamo per un determinato controller o azione:

public class ClaimRequirementAttribute : TypeFilterAttribute
{
    public ClaimRequirementAttribute(string claimType, string claimValue) : base(typeof(ClaimRequirementFilter))
    {
        Arguments = new object[] {new Claim(claimType, claimValue) };
    }
}

public class ClaimRequirementFilter : IAuthorizationFilter
{
    readonly Claim _claim;

    public ClaimRequirementFilter(Claim claim)
    {
        _claim = claim;
    }

    public void OnAuthorization(AuthorizationFilterContext context)
    {
        var hasClaim = context.HttpContext.User.Claims.Any(c => c.Type == _claim.Type && c.Value == _claim.Value);
        if (!hasClaim)
        {
            context.Result = new ForbidResult();
        }
    }
}


[Route("api/resource")]
public class MyController : Controller
{
    [ClaimRequirement(MyClaimTypes.Permission, "CanReadResource")]
    [HttpGet]
    public IActionResult GetResource()
    {
        return Ok();
    }
}

Se qualcuno desidera semplicemente convalidare un token di connessione nella fase di autorizzazione utilizzando le pratiche di sicurezza correnti che puoi,

aggiungilo ai tuoi Startup / ConfigureServices

 services.AddSingleton<IAuthorizationHandler, BearerAuthorizationHandler>(); services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme).AddJwtBearer(); services.AddAuthorization(options => options.AddPolicy("Bearer", policy => policy.AddRequirements(new BearerRequirement()) ) ); 

e questo nella tua base di codice,

 public class BearerRequirement : IAuthorizationRequirement { public async Task<bool> IsTokenValid(SomeValidationContext context, string token) { // here you can check if the token received is valid return true; } } public class BearerAuthorizationHandler : AuthorizationHandler<BearerRequirement> { public BearerAuthorizationHandler(SomeValidationContext thatYouCanInject) { ... } protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, BearerRequirement requirement) { var authFilterCtx = (Microsoft.AspNetCore.Mvc.Filters.AuthorizationFilterContext)context.Resource; string authHeader = authFilterCtx.HttpContext.Request.Headers["Authorization"]; if (authHeader != null && authHeader.Contains("Bearer")) { var token = authHeader.Replace("Bearer ", string.Empty); if (await requirement.IsTokenValid(thatYouCanInject, token)) { context.Succeed(requirement); } } } } 

Se il codice non raggiunge il context.Succeed(...) fallirà comunque (401).

E poi nei controller puoi usare

 [Authorize(Policy = "Bearer", AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)] 

Sono la persona di sicurezza asp.net. Innanzi tutto, mi scuso che nulla di tutto ciò sia documentato al di fuori del campione del negozio di musica o dei test unitari, e tutto è ancora in fase di perfezionamento in termini di API esposte. La documentazione dettagliata è here .

Non vogliamo che tu scriva attributi di autorizzazione personalizzati. Se devi farlo, abbiamo fatto qualcosa di sbagliato. Invece, dovresti scrivere i requisiti di autorizzazione.

L'autorizzazione agisce sulle identità. Le identità vengono create mediante autenticazione.

Dici nei commenti che vuoi controllare un ID di sessione in un'intestazione. L'ID della sessione sarebbe la base per l'identità. Se si desidera utilizzare l'attributo Authorize si scriverà un middleware di autenticazione per prendere quell'intestazione e trasformarlo in un ClaimsPrincipal autenticato. Verificherebbe quindi che all'interno di un requisito di autorizzazione. I requisiti di autorizzazione possono essere complicati a piacimento, ad esempio eccone uno che richiede una data di richiesta di nascita sull'identità attuale e autorizzerà se l'utente ha più di 18 anni;

public class Over18Requirement : AuthorizationHandler<Over18Requirement>, IAuthorizationRequirement
{
        public override void Handle(AuthorizationHandlerContext context, Over18Requirement requirement)
        {
            if (!context.User.HasClaim(c => c.Type == ClaimTypes.DateOfBirth))
            {
                context.Fail();
                return;
            }

            var dateOfBirth = Convert.ToDateTime(context.User.FindFirst(c => c.Type == ClaimTypes.DateOfBirth).Value);
            int age = DateTime.Today.Year - dateOfBirth.Year;
            if (dateOfBirth > DateTime.Today.AddYears(-age))
            {
                age--;
            }

            if (age >= 18)
            {
                context.Succeed(requirement);
            }
            else
            {
                context.Fail();
            }
        }
    }
}

Quindi nella funzione ConfigureServices() lo collegheresti

services.AddAuthorization(options =>
{
    options.AddPolicy("Over18", 
        policy => policy.Requirements.Add(new Authorization.Over18Requirement()));
});

E infine, applicalo a un controller o a un metodo di azione con

[Authorize(Policy = "Over18")]




asp.net-core-mvc