Condividi tramite


Provider di criteri di autorizzazione personalizzati che usano IAuthorizationPolicyProvider in ASP.NET Core

Di Mike Rousos

In genere, quando si usa l'autorizzazione basata su criteri, i criteri vengono registrati chiamando AuthorizationOptions.AddPolicy come parte della configurazione del servizio di autorizzazione. In alcuni scenari, potrebbe non essere possibile (o auspicabile) registrare tutti i criteri di autorizzazione in questo modo. In questi casi, è possibile usare un oggetto personalizzato per controllare la modalità di fornitura IAuthorizationPolicyProvider dei criteri di autorizzazione.

Esempi di scenari in cui un'operatore personalizzato IAuthorizationPolicyProvider può essere utile includono:

  • Uso di un servizio esterno per fornire la valutazione dei criteri.
  • L'uso di un'ampia gamma di criteri (ad esempio per numeri di sala o età diversi), quindi non ha senso aggiungere ogni singolo criterio di autorizzazione con una AuthorizationOptions.AddPolicy chiamata.
  • Creazione di criteri in fase di esecuzione in base alle informazioni in un'origine dati esterna (ad esempio un database) o alla determinazione dinamica dei requisiti di autorizzazione tramite un altro meccanismo.

Visualizzare o scaricare il codice di esempio dal repository GitHub AspNetCore. Scaricare il file ZIP del repository dotnet/AspNetCore. Decomprimere il file . Passare alla cartella del progetto src/Security/samples/CustomPolicyProvider .

Personalizzare il recupero dei criteri

ASP.NET le app di base usano un'implementazione dell'interfaccia IAuthorizationPolicyProvider per recuperare i criteri di autorizzazione. Per impostazione predefinita, DefaultAuthorizationPolicyProvider viene registrato e usato. DefaultAuthorizationPolicyProvider restituisce i criteri dall'oggetto AuthorizationOptions fornito in una IServiceCollection.AddAuthorization chiamata.

Personalizzare questo comportamento registrando un'implementazione diversa IAuthorizationPolicyProvider nel contenitore di inserimento delle dipendenze dell'app.

L'interfaccia IAuthorizationPolicyProvider contiene tre API:

  • GetPolicyAsync restituisce un criterio di autorizzazione per un determinato nome.
  • GetDefaultPolicyAsync restituisce i criteri di autorizzazione predefiniti (i criteri usati per [Authorize] gli attributi senza un criterio specificato).
  • GetFallbackPolicyAsync restituisce i criteri di autorizzazione di fallback (i criteri usati dal middleware di autorizzazione quando non viene specificato alcun criterio).

Implementando queste API, è possibile personalizzare il modo in cui vengono forniti i criteri di autorizzazione.

Esempio di attributo authorize con parametri

Uno scenario in cui IAuthorizationPolicyProvider è utile è abilitare attributi personalizzati [Authorize] i cui requisiti dipendono da un parametro. Nella documentazione sull'autorizzazione basata su criteri, ad esempio, un criterio basato sull'età ("AtLeast21") è stato usato come esempio. Se diverse azioni del controller in un'app devono essere rese disponibili agli utenti di età diverse , potrebbe essere utile avere molti criteri diversi basati sull'età. Anziché registrare tutti i diversi criteri basati sull'età necessari per l'applicazione in AuthorizationOptions, è possibile generare i criteri in modo dinamico con un oggetto personalizzato IAuthorizationPolicyProvider. Per semplificare l'uso dei criteri, è possibile annotare le azioni con l'attributo di autorizzazione personalizzato, ad esempio [MinimumAgeAuthorize(20)].

Attributi di autorizzazione personalizzati

I criteri di autorizzazione vengono identificati dai relativi nomi. L'oggetto personalizzato MinimumAgeAuthorizeAttribute descritto in precedenza deve eseguire il mapping degli argomenti in una stringa che può essere usata per recuperare i criteri di autorizzazione corrispondenti. A tale scopo, è possibile derivare da AuthorizeAttribute e eseguire il wrapping della AuthorizeAttribute.PolicyAge proprietà.

internal class MinimumAgeAuthorizeAttribute : AuthorizeAttribute
{
    const string POLICY_PREFIX = "MinimumAge";

    public MinimumAgeAuthorizeAttribute(int age) => Age = age;

    // Get or set the Age property by manipulating the underlying Policy property
    public int Age
    {
        get
        {
            if (int.TryParse(Policy.Substring(POLICY_PREFIX.Length), out var age))
            {
                return age;
            }
            return default(int);
        }
        set
        {
            Policy = $"{POLICY_PREFIX}{value.ToString()}";
        }
    }
}

Questo tipo di attributo ha una Policy stringa basata sul prefisso hardcoded ("MinimumAge") e un numero intero passato tramite il costruttore .

È possibile applicarlo alle azioni nello stesso modo degli altri Authorize attributi, ad eccezione del fatto che accetta un numero intero come parametro.

[MinimumAgeAuthorize(10)]
public IActionResult RequiresMinimumAge10()

IAuthorizationPolicyProvider personalizzato

Il personalizzato MinimumAgeAuthorizeAttribute semplifica la richiesta di criteri di autorizzazione per qualsiasi età minima desiderata. Il problema successivo da risolvere è assicurarsi che i criteri di autorizzazione siano disponibili per tutte le diverse età. Qui è utile un oggetto IAuthorizationPolicyProvider .

Quando si usa MinimumAgeAuthorizationAttribute, i nomi dei criteri di autorizzazione seguiranno il modello "MinimumAge" + Age, in modo che l'utente personalizzato IAuthorizationPolicyProvider generi criteri di autorizzazione tramite:

  • Analisi dell'età dal nome del criterio.
  • Uso AuthorizationPolicyBuilder di per creare un nuovo AuthorizationPolicy
  • In questo e negli esempi seguenti si presuppone che l'utente sia autenticato tramite .cookie Deve AuthorizationPolicyBuilder essere costruito con almeno un nome di schema di autorizzazione o avere sempre esito positivo. In caso contrario, non sono disponibili informazioni su come fornire una richiesta all'utente e verrà generata un'eccezione.
  • Aggiunta di requisiti ai criteri in base all'età con AuthorizationPolicyBuilder.AddRequirements. In altri scenari, è possibile usare RequireClaiminvece , RequireRoleo RequireUserName .
internal class MinimumAgePolicyProvider : IAuthorizationPolicyProvider
{
    const string POLICY_PREFIX = "MinimumAge";

    // Policies are looked up by string name, so expect 'parameters' (like age)
    // to be embedded in the policy names. This is abstracted away from developers
    // by the more strongly-typed attributes derived from AuthorizeAttribute
    // (like [MinimumAgeAuthorize()] in this sample)
    public Task<AuthorizationPolicy> GetPolicyAsync(string policyName)
    {
        if (policyName.StartsWith(POLICY_PREFIX, StringComparison.OrdinalIgnoreCase) &&
            int.TryParse(policyName.Substring(POLICY_PREFIX.Length), out var age))
        {
            var policy = new AuthorizationPolicyBuilder(CookieAuthenticationDefaults.AuthenticationScheme);
            policy.AddRequirements(new MinimumAgeRequirement(age));
            return Task.FromResult(policy.Build());
        }

        return Task.FromResult<AuthorizationPolicy>(null);
    }
}

Più provider di criteri di autorizzazione

Quando si usano implementazioni personalizzate IAuthorizationPolicyProvider , tenere presente che ASP.NET Core usa solo un'istanza di IAuthorizationPolicyProvider. Se un provider personalizzato non è in grado di fornire criteri di autorizzazione per tutti i nomi di criteri che verranno usati, deve rinviare a un provider di backup.

Si consideri, ad esempio, un'applicazione che necessita sia di criteri di età personalizzati che di recupero dei criteri basati su ruoli più tradizionali. Un'app di questo tipo può usare un provider di criteri di autorizzazione personalizzato che:

  • Tenta di analizzare i nomi dei criteri.
  • Chiama in un provider di criteri diverso (ad esempio DefaultAuthorizationPolicyProvider) se il nome del criterio non contiene un'età.

L'implementazione di esempio IAuthorizationPolicyProvider illustrata in precedenza può essere aggiornata per l'uso DefaultAuthorizationPolicyProvider di creando un provider di criteri di backup nel relativo costruttore (da usare nel caso in cui il nome del criterio non corrisponda al modello previsto di 'MinimumAge' + age).

private DefaultAuthorizationPolicyProvider BackupPolicyProvider { get; }

public MinimumAgePolicyProvider(IOptions<AuthorizationOptions> options)
{
    // ASP.NET Core only uses one authorization policy provider, so if the custom implementation
    // doesn't handle all policies it should fall back to an alternate provider.
    BackupPolicyProvider = new DefaultAuthorizationPolicyProvider(options);
}

È quindi possibile aggiornare il GetPolicyAsync metodo per usare anziché BackupPolicyProvider restituire null:

...
return BackupPolicyProvider.GetPolicyAsync(policyName);

Criteri predefiniti

Oltre a fornire criteri di autorizzazione denominati, un personalizzato IAuthorizationPolicyProvider deve implementare GetDefaultPolicyAsync per fornire criteri di autorizzazione per [Authorize] gli attributi senza un nome di criterio specificato.

In molti casi, questo attributo di autorizzazione richiede solo un utente autenticato, in modo da poter effettuare i criteri necessari con una chiamata a RequireAuthenticatedUser:

public Task<AuthorizationPolicy> GetDefaultPolicyAsync() => 
    Task.FromResult(new AuthorizationPolicyBuilder(CookieAuthenticationDefaults.AuthenticationScheme).RequireAuthenticatedUser().Build());

Come per tutti gli aspetti di un oggetto personalizzato IAuthorizationPolicyProvider, è possibile personalizzare questo aspetto, in base alle esigenze. In alcuni casi, potrebbe essere consigliabile recuperare i criteri predefiniti da un fallback IAuthorizationPolicyProvider.

Criteri di fallback

Un oggetto personalizzato IAuthorizationPolicyProvider può facoltativamente implementare GetFallbackPolicyAsync per fornire un criterio usato durante la combinazione di criteri e quando non vengono specificati criteri. Se GetFallbackPolicyAsync restituisce un criterio non Null, il criterio restituito viene usato dal middleware di autorizzazione quando non vengono specificati criteri per la richiesta.

Se non sono necessari criteri di fallback, il provider può restituire null o rinviare al provider di fallback:

public Task<AuthorizationPolicy> GetFallbackPolicyAsync() => 
    Task.FromResult<AuthorizationPolicy>(null);

Usare un IAuthorizationPolicyProvider personalizzato

Per usare criteri personalizzati da un oggetto IAuthorizationPolicyProvider, è necessario:

  • Registrare i tipi appropriati AuthorizationHandler con l'inserimento delle dipendenze (descritto in Autorizzazione basata su criteri), come per tutti gli scenari di autorizzazione basati su criteri.

  • Registrare il tipo personalizzato IAuthorizationPolicyProvider nella raccolta di servizi di inserimento delle dipendenze dell'app in Startup.ConfigureServices per sostituire il provider di criteri predefinito.

    services.AddSingleton<IAuthorizationPolicyProvider, MinimumAgePolicyProvider>();
    

Un esempio personalizzato IAuthorizationPolicyProvider completo è disponibile nel repository GitHub dotnet/aspnetcore.