Autorizzazione basata sulle risorse in ASP.NET Core

L'approccio di autorizzazione dipende dalla risorsa. Ad esempio, solo l'autore di un documento è autorizzato ad aggiornare il documento. Di conseguenza, il documento deve essere recuperato dall'archivio dati prima che possa verificarsi la valutazione dell'autorizzazione.

La valutazione degli attributi viene eseguita prima del data binding e prima dell'esecuzione del gestore di pagine o dell'azione che carica il documento. Per questi motivi, l'autorizzazione dichiarativa con un [Authorize] attributo non è sufficiente. È invece possibile richiamare un metodo di autorizzazione personalizzato, ovvero uno stile noto come autorizzazione imperativa.

Visualizzare o scaricare il codice di esempio (procedura per il download).

Creare un'app ASP.NET Core con i dati utente protetti dall'autorizzazione contiene un'app di esempio che usa l'autorizzazione basata sulle risorse.

Usare l'autorizzazione imperativa

L'autorizzazione viene implementata come IAuthorizationService servizio e viene registrata nella raccolta di servizi all'avvio dell'applicazione. Il servizio viene reso disponibile tramite l'inserimento delle dipendenze a gestori di pagine o azioni.

public class DocumentController : Controller
{
    private readonly IAuthorizationService _authorizationService;
    private readonly IDocumentRepository _documentRepository;

    public DocumentController(IAuthorizationService authorizationService,
                              IDocumentRepository documentRepository)
    {
        _authorizationService = authorizationService;
        _documentRepository = documentRepository;
    }

IAuthorizationService ha due AuthorizeAsync overload del metodo: uno che accetta la risorsa e il nome del criterio e l'altro accettare la risorsa e un elenco di requisiti da valutare.

Task<AuthorizationResult> AuthorizeAsync(ClaimsPrincipal user,
                          object resource,
                          IEnumerable<IAuthorizationRequirement> requirements);
Task<AuthorizationResult> AuthorizeAsync(ClaimsPrincipal user,
                          object resource,
                          string policyName);

Nell'esempio seguente la risorsa da proteggere viene caricata in un oggetto personalizzato Document . Viene richiamato un AuthorizeAsync overload per determinare se l'utente corrente è autorizzato a modificare il documento fornito. Un criterio di autorizzazione personalizzato "EditPolicy" viene inserito nella decisione. Per altre informazioni sulla creazione di criteri di autorizzazione, vedere Autorizzazione personalizzata basata su criteri.

Nota

Gli esempi di codice seguenti presuppongono che l'autenticazione sia stata eseguita e impostata la User proprietà .

public async Task<IActionResult> OnGetAsync(Guid documentId)
{
    Document = _documentRepository.Find(documentId);

    if (Document == null)
    {
        return new NotFoundResult();
    }

    var authorizationResult = await _authorizationService
            .AuthorizeAsync(User, Document, "EditPolicy");

    if (authorizationResult.Succeeded)
    {
        return Page();
    }
    else if (User.Identity.IsAuthenticated)
    {
        return new ForbidResult();
    }
    else
    {
        return new ChallengeResult();
    }
}

Scrivere un gestore basato su risorse

La scrittura di un gestore per l'autorizzazione basata su risorse non è molto diversa dalla scrittura di un gestore dei requisiti semplici. Creare una classe di requisiti personalizzata e implementare una classe del gestore dei requisiti. Per altre informazioni sulla creazione di una classe di requisiti, vedere Requisiti.

La classe del gestore specifica sia il requisito che il tipo di risorsa. Ad esempio, un gestore che usa e SameAuthorRequirement una Document risorsa segue:

public class DocumentAuthorizationHandler : 
    AuthorizationHandler<SameAuthorRequirement, Document>
{
    protected override Task HandleRequirementAsync(AuthorizationHandlerContext context,
                                                   SameAuthorRequirement requirement,
                                                   Document resource)
    {
        if (context.User.Identity?.Name == resource.Author)
        {
            context.Succeed(requirement);
        }

        return Task.CompletedTask;
    }
}

public class SameAuthorRequirement : IAuthorizationRequirement { }

Nell'esempio precedente si supponga che SameAuthorRequirement sia un caso speciale di una classe più generica SpecificAuthorRequirement . La SpecificAuthorRequirement classe (non visualizzata) contiene una Name proprietà che rappresenta il nome dell'autore. La Name proprietà può essere impostata sull'utente corrente.

Registrare il requisito e il gestore in Program.cs:

builder.Services.AddRazorPages();
builder.Services.AddControllersWithViews();

builder.Services.AddAuthorization(options =>
{
    options.AddPolicy("EditPolicy", policy =>
        policy.Requirements.Add(new SameAuthorRequirement()));
});

builder.Services.AddSingleton<IAuthorizationHandler, DocumentAuthorizationHandler>();
builder.Services.AddSingleton<IAuthorizationHandler, DocumentAuthorizationCrudHandler>();
builder.Services.AddScoped<IDocumentRepository, DocumentRepository>();

Requisiti operativi

Se si stanno prendendo decisioni in base ai risultati delle operazioni CRUD (Create, Read, Update, Delete), usare la OperationAuthorizationRequirement classe helper. Questa classe consente di scrivere un singolo gestore anziché una singola classe per ogni tipo di operazione. Per usarlo, specificare alcuni nomi di operazione:

public static class Operations
{
    public static OperationAuthorizationRequirement Create =
        new OperationAuthorizationRequirement { Name = nameof(Create) };
    public static OperationAuthorizationRequirement Read =
        new OperationAuthorizationRequirement { Name = nameof(Read) };
    public static OperationAuthorizationRequirement Update =
        new OperationAuthorizationRequirement { Name = nameof(Update) };
    public static OperationAuthorizationRequirement Delete =
        new OperationAuthorizationRequirement { Name = nameof(Delete) };
}

Il gestore viene implementato come segue, usando un OperationAuthorizationRequirement requisito e una Document risorsa:

public class DocumentAuthorizationCrudHandler :
    AuthorizationHandler<OperationAuthorizationRequirement, Document>
{
    protected override Task HandleRequirementAsync(AuthorizationHandlerContext context,
                                                   OperationAuthorizationRequirement requirement,
                                                   Document resource)
    {
        if (context.User.Identity?.Name == resource.Author &&
            requirement.Name == Operations.Read.Name)
        {
            context.Succeed(requirement);
        }

        return Task.CompletedTask;
    }
}

Il gestore precedente convalida l'operazione usando la risorsa, l'utente identitye la proprietà del Name requisito.

Verifica e proibizione con un gestore di risorse operative

In questa sezione viene illustrato come vengono elaborati i risultati delle azioni di verifica e proibizione e il modo in cui la sfida e la proibizione differiscono.

Per chiamare un gestore di risorse operative, specificare l'operazione quando si richiama AuthorizeAsync il gestore o l'azione della pagina. Nell'esempio seguente viene determinato se l'utente autenticato è autorizzato a visualizzare il documento fornito.

Nota

Gli esempi di codice seguenti presuppongono che l'autenticazione sia stata eseguita e impostata la User proprietà .

public async Task<IActionResult> OnGetAsync(Guid documentId)
{
    Document = _documentRepository.Find(documentId);

    if (Document == null)
    {
        return new NotFoundResult();
    }

    var authorizationResult = await _authorizationService
            .AuthorizeAsync(User, Document, Operations.Read);

    if (authorizationResult.Succeeded)
    {
        return Page();
    }
    else if (User.Identity.IsAuthenticated)
    {
        return new ForbidResult();
    }
    else
    {
        return new ChallengeResult();
    }
}

Se l'autorizzazione ha esito positivo, viene restituita la pagina per la visualizzazione del documento. Se l'autorizzazione ha esito negativo ma l'utente viene autenticato, la restituzione ForbidResult informa qualsiasi middleware di autenticazione che l'autorizzazione non è riuscita. Un ChallengeResult viene restituito quando è necessario eseguire l'autenticazione. Per i client browser interattivi, potrebbe essere opportuno reindirizzare l'utente a una pagina di accesso.

L'approccio di autorizzazione dipende dalla risorsa. Ad esempio, solo l'autore di un documento è autorizzato ad aggiornare il documento. Di conseguenza, il documento deve essere recuperato dall'archivio dati prima che possa verificarsi la valutazione dell'autorizzazione.

La valutazione degli attributi viene eseguita prima del data binding e prima dell'esecuzione del gestore di pagine o dell'azione che carica il documento. Per questi motivi, l'autorizzazione dichiarativa con un [Authorize] attributo non è sufficiente. È invece possibile richiamare un metodo di autorizzazione personalizzato, ovvero uno stile noto come autorizzazione imperativa.

Visualizzare o scaricare il codice di esempio (procedura per il download).

Creare un'app ASP.NET Core con i dati utente protetti dall'autorizzazione contiene un'app di esempio che usa l'autorizzazione basata sulle risorse.

Usare l'autorizzazione imperativa

L'autorizzazione viene implementata come IAuthorizationService servizio e viene registrata nella raccolta di servizi all'interno della Startup classe . Il servizio viene reso disponibile tramite l'inserimento delle dipendenze a gestori di pagine o azioni.

public class DocumentController : Controller
{
    private readonly IAuthorizationService _authorizationService;
    private readonly IDocumentRepository _documentRepository;

    public DocumentController(IAuthorizationService authorizationService,
                              IDocumentRepository documentRepository)
    {
        _authorizationService = authorizationService;
        _documentRepository = documentRepository;
    }

IAuthorizationService ha due AuthorizeAsync overload del metodo: uno che accetta la risorsa e il nome del criterio e l'altro accettare la risorsa e un elenco di requisiti da valutare.

Task<AuthorizationResult> AuthorizeAsync(ClaimsPrincipal user,
                          object resource,
                          IEnumerable<IAuthorizationRequirement> requirements);
Task<AuthorizationResult> AuthorizeAsync(ClaimsPrincipal user,
                          object resource,
                          string policyName);

Nell'esempio seguente la risorsa da proteggere viene caricata in un oggetto personalizzato Document . Viene richiamato un AuthorizeAsync overload per determinare se l'utente corrente è autorizzato a modificare il documento fornito. Un criterio di autorizzazione personalizzato "EditPolicy" viene inserito nella decisione. Per altre informazioni sulla creazione di criteri di autorizzazione, vedere Autorizzazione personalizzata basata su criteri.

Nota

Gli esempi di codice seguenti presuppongono che l'autenticazione sia stata eseguita e impostata la User proprietà .

public async Task<IActionResult> OnGetAsync(Guid documentId)
{
    Document = _documentRepository.Find(documentId);

    if (Document == null)
    {
        return new NotFoundResult();
    }

    var authorizationResult = await _authorizationService
            .AuthorizeAsync(User, Document, "EditPolicy");

    if (authorizationResult.Succeeded)
    {
        return Page();
    }
    else if (User.Identity.IsAuthenticated)
    {
        return new ForbidResult();
    }
    else
    {
        return new ChallengeResult();
    }
}

Scrivere un gestore basato su risorse

La scrittura di un gestore per l'autorizzazione basata su risorse non è molto diversa dalla scrittura di un gestore dei requisiti semplici. Creare una classe di requisiti personalizzata e implementare una classe del gestore dei requisiti. Per altre informazioni sulla creazione di una classe di requisiti, vedere Requisiti.

La classe del gestore specifica sia il requisito che il tipo di risorsa. Ad esempio, un gestore che usa e SameAuthorRequirement una Document risorsa segue:

public class DocumentAuthorizationHandler : 
    AuthorizationHandler<SameAuthorRequirement, Document>
{
    protected override Task HandleRequirementAsync(AuthorizationHandlerContext context,
                                                   SameAuthorRequirement requirement,
                                                   Document resource)
    {
        if (context.User.Identity?.Name == resource.Author)
        {
            context.Succeed(requirement);
        }

        return Task.CompletedTask;
    }
}

public class SameAuthorRequirement : IAuthorizationRequirement { }

Nell'esempio precedente si supponga che SameAuthorRequirement sia un caso speciale di una classe più generica SpecificAuthorRequirement . La SpecificAuthorRequirement classe (non visualizzata) contiene una Name proprietà che rappresenta il nome dell'autore. La Name proprietà può essere impostata sull'utente corrente.

Registrare il requisito e il gestore in Startup.ConfigureServices:

services.AddControllersWithViews();
services.AddRazorPages();

services.AddAuthorization(options =>
{
    options.AddPolicy("EditPolicy", policy =>
        policy.Requirements.Add(new SameAuthorRequirement()));
});

services.AddSingleton<IAuthorizationHandler, DocumentAuthorizationHandler>();
services.AddSingleton<IAuthorizationHandler, DocumentAuthorizationCrudHandler>();
services.AddScoped<IDocumentRepository, DocumentRepository>();

Requisiti operativi

Se si stanno prendendo decisioni in base ai risultati delle operazioni CRUD (Create, Read, Update, Delete), usare la OperationAuthorizationRequirement classe helper. Questa classe consente di scrivere un singolo gestore anziché una singola classe per ogni tipo di operazione. Per usarlo, specificare alcuni nomi di operazione:

public static class Operations
{
    public static OperationAuthorizationRequirement Create =
        new OperationAuthorizationRequirement { Name = nameof(Create) };
    public static OperationAuthorizationRequirement Read =
        new OperationAuthorizationRequirement { Name = nameof(Read) };
    public static OperationAuthorizationRequirement Update =
        new OperationAuthorizationRequirement { Name = nameof(Update) };
    public static OperationAuthorizationRequirement Delete =
        new OperationAuthorizationRequirement { Name = nameof(Delete) };
}

Il gestore viene implementato come segue, usando un OperationAuthorizationRequirement requisito e una Document risorsa:

public class DocumentAuthorizationCrudHandler :
    AuthorizationHandler<OperationAuthorizationRequirement, Document>
{
    protected override Task HandleRequirementAsync(AuthorizationHandlerContext context,
                                                   OperationAuthorizationRequirement requirement,
                                                   Document resource)
    {
        if (context.User.Identity?.Name == resource.Author &&
            requirement.Name == Operations.Read.Name)
        {
            context.Succeed(requirement);
        }

        return Task.CompletedTask;
    }
}

Il gestore precedente convalida l'operazione usando la risorsa, l'utente identitye la proprietà del Name requisito.

Verifica e proibizione con un gestore di risorse operative

In questa sezione viene illustrato come vengono elaborati i risultati delle azioni di verifica e proibizione e il modo in cui la sfida e la proibizione differiscono.

Per chiamare un gestore di risorse operative, specificare l'operazione quando si richiama AuthorizeAsync il gestore o l'azione della pagina. Nell'esempio seguente viene determinato se l'utente autenticato è autorizzato a visualizzare il documento fornito.

Nota

Gli esempi di codice seguenti presuppongono che l'autenticazione sia stata eseguita e impostata la User proprietà .

public async Task<IActionResult> OnGetAsync(Guid documentId)
{
    Document = _documentRepository.Find(documentId);

    if (Document == null)
    {
        return new NotFoundResult();
    }

    var authorizationResult = await _authorizationService
            .AuthorizeAsync(User, Document, Operations.Read);

    if (authorizationResult.Succeeded)
    {
        return Page();
    }
    else if (User.Identity.IsAuthenticated)
    {
        return new ForbidResult();
    }
    else
    {
        return new ChallengeResult();
    }
}

Se l'autorizzazione ha esito positivo, viene restituita la pagina per la visualizzazione del documento. Se l'autorizzazione ha esito negativo ma l'utente viene autenticato, la restituzione ForbidResult informa qualsiasi middleware di autenticazione che l'autorizzazione non è riuscita. Un ChallengeResult viene restituito quando è necessario eseguire l'autenticazione. Per i client browser interattivi, potrebbe essere opportuno reindirizzare l'utente a una pagina di accesso.

L'approccio di autorizzazione dipende dalla risorsa. Ad esempio, solo l'autore di un documento è autorizzato ad aggiornare il documento. Di conseguenza, il documento deve essere recuperato dall'archivio dati prima che possa verificarsi la valutazione dell'autorizzazione.

La valutazione degli attributi viene eseguita prima del data binding e prima dell'esecuzione del gestore di pagine o dell'azione che carica il documento. Per questi motivi, l'autorizzazione dichiarativa con un [Authorize] attributo non è sufficiente. È invece possibile richiamare un metodo di autorizzazione personalizzato, ovvero uno stile noto come autorizzazione imperativa.

Visualizzare o scaricare il codice di esempio (procedura per il download).

Creare un'app ASP.NET Core con i dati utente protetti dall'autorizzazione contiene un'app di esempio che usa l'autorizzazione basata sulle risorse.

Usare l'autorizzazione imperativa

L'autorizzazione viene implementata come IAuthorizationService servizio e viene registrata nella raccolta di servizi all'interno della Startup classe . Il servizio viene reso disponibile tramite l'inserimento delle dipendenze a gestori di pagine o azioni.

public class DocumentController : Controller
{
    private readonly IAuthorizationService _authorizationService;
    private readonly IDocumentRepository _documentRepository;

    public DocumentController(IAuthorizationService authorizationService,
                              IDocumentRepository documentRepository)
    {
        _authorizationService = authorizationService;
        _documentRepository = documentRepository;
    }

IAuthorizationService ha due AuthorizeAsync overload del metodo: uno che accetta la risorsa e il nome del criterio e l'altro accettare la risorsa e un elenco di requisiti da valutare.

Task<AuthorizationResult> AuthorizeAsync(ClaimsPrincipal user,
                          object resource,
                          IEnumerable<IAuthorizationRequirement> requirements);
Task<AuthorizationResult> AuthorizeAsync(ClaimsPrincipal user,
                          object resource,
                          string policyName);

Nell'esempio seguente la risorsa da proteggere viene caricata in un oggetto personalizzato Document . Viene richiamato un AuthorizeAsync overload per determinare se l'utente corrente è autorizzato a modificare il documento fornito. Un criterio di autorizzazione personalizzato "EditPolicy" viene inserito nella decisione. Per altre informazioni sulla creazione di criteri di autorizzazione, vedere Autorizzazione personalizzata basata su criteri.

Nota

Gli esempi di codice seguenti presuppongono che l'autenticazione sia stata eseguita e impostata la User proprietà .

public async Task<IActionResult> OnGetAsync(Guid documentId)
{
    Document = _documentRepository.Find(documentId);

    if (Document == null)
    {
        return new NotFoundResult();
    }

    var authorizationResult = await _authorizationService
            .AuthorizeAsync(User, Document, "EditPolicy");

    if (authorizationResult.Succeeded)
    {
        return Page();
    }
    else if (User.Identity.IsAuthenticated)
    {
        return new ForbidResult();
    }
    else
    {
        return new ChallengeResult();
    }
}

Scrivere un gestore basato su risorse

La scrittura di un gestore per l'autorizzazione basata su risorse non è molto diversa dalla scrittura di un gestore dei requisiti semplici. Creare una classe di requisiti personalizzata e implementare una classe del gestore dei requisiti. Per altre informazioni sulla creazione di una classe di requisiti, vedere Requisiti.

La classe del gestore specifica sia il requisito che il tipo di risorsa. Ad esempio, un gestore che usa e SameAuthorRequirement una Document risorsa segue:

public class DocumentAuthorizationHandler : 
    AuthorizationHandler<SameAuthorRequirement, Document>
{
    protected override Task HandleRequirementAsync(AuthorizationHandlerContext context,
                                                   SameAuthorRequirement requirement,
                                                   Document resource)
    {
        if (context.User.Identity?.Name == resource.Author)
        {
            context.Succeed(requirement);
        }

        return Task.CompletedTask;
    }
}

public class SameAuthorRequirement : IAuthorizationRequirement { }

Nell'esempio precedente si supponga che SameAuthorRequirement sia un caso speciale di una classe più generica SpecificAuthorRequirement . La SpecificAuthorRequirement classe (non visualizzata) contiene una Name proprietà che rappresenta il nome dell'autore. La Name proprietà può essere impostata sull'utente corrente.

Registrare il requisito e il gestore in Startup.ConfigureServices:

services.AddMvc();

services.AddAuthorization(options =>
{
    options.AddPolicy("EditPolicy", policy =>
        policy.Requirements.Add(new SameAuthorRequirement()));
});

services.AddSingleton<IAuthorizationHandler, DocumentAuthorizationHandler>();
services.AddSingleton<IAuthorizationHandler, DocumentAuthorizationCrudHandler>();
services.AddScoped<IDocumentRepository, DocumentRepository>();

Requisiti operativi

Se si stanno prendendo decisioni in base ai risultati delle operazioni CRUD (Create, Read, Update, Delete), usare la OperationAuthorizationRequirement classe helper. Questa classe consente di scrivere un singolo gestore anziché una singola classe per ogni tipo di operazione. Per usarlo, specificare alcuni nomi di operazione:

public static class Operations
{
    public static OperationAuthorizationRequirement Create =
        new OperationAuthorizationRequirement { Name = nameof(Create) };
    public static OperationAuthorizationRequirement Read =
        new OperationAuthorizationRequirement { Name = nameof(Read) };
    public static OperationAuthorizationRequirement Update =
        new OperationAuthorizationRequirement { Name = nameof(Update) };
    public static OperationAuthorizationRequirement Delete =
        new OperationAuthorizationRequirement { Name = nameof(Delete) };
}

Il gestore viene implementato come segue, usando un OperationAuthorizationRequirement requisito e una Document risorsa:

public class DocumentAuthorizationCrudHandler :
    AuthorizationHandler<OperationAuthorizationRequirement, Document>
{
    protected override Task HandleRequirementAsync(AuthorizationHandlerContext context,
                                                   OperationAuthorizationRequirement requirement,
                                                   Document resource)
    {
        if (context.User.Identity?.Name == resource.Author &&
            requirement.Name == Operations.Read.Name)
        {
            context.Succeed(requirement);
        }

        return Task.CompletedTask;
    }
}

Il gestore precedente convalida l'operazione usando la risorsa, l'utente identitye la proprietà del Name requisito.

Verifica e proibizione con un gestore di risorse operative

In questa sezione viene illustrato come vengono elaborati i risultati delle azioni di verifica e proibizione e il modo in cui la sfida e la proibizione differiscono.

Per chiamare un gestore di risorse operative, specificare l'operazione quando si richiama AuthorizeAsync il gestore o l'azione della pagina. Nell'esempio seguente viene determinato se l'utente autenticato è autorizzato a visualizzare il documento fornito.

Nota

Gli esempi di codice seguenti presuppongono che l'autenticazione sia stata eseguita e impostata la User proprietà .

public async Task<IActionResult> OnGetAsync(Guid documentId)
{
    Document = _documentRepository.Find(documentId);

    if (Document == null)
    {
        return new NotFoundResult();
    }

    var authorizationResult = await _authorizationService
            .AuthorizeAsync(User, Document, Operations.Read);

    if (authorizationResult.Succeeded)
    {
        return Page();
    }
    else if (User.Identity.IsAuthenticated)
    {
        return new ForbidResult();
    }
    else
    {
        return new ChallengeResult();
    }
}

Se l'autorizzazione ha esito positivo, viene restituita la pagina per la visualizzazione del documento. Se l'autorizzazione ha esito negativo ma l'utente viene autenticato, la restituzione ForbidResult informa qualsiasi middleware di autenticazione che l'autorizzazione non è riuscita. Un ChallengeResult viene restituito quando è necessario eseguire l'autenticazione. Per i client browser interattivi, potrebbe essere opportuno reindirizzare l'utente a una pagina di accesso.