Condividi tramite


Autenticazione e autorizzazione in gRPC per ASP.NET Core

Note

Questa non è la versione più recente di questo articolo. Per la versione corrente, vedere la versione .NET 10 di questo articolo.

Warning

Questa versione di ASP.NET Core non è più supportata. Per altre informazioni, vedere i criteri di supporto di .NET e .NET Core. Per la versione corrente, vedere la versione .NET 10 di questo articolo.

Di James Newton-King

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

Autenticare gli utenti che chiamano un servizio gRPC

gRPC può essere usato con ASP.NET Core authentication per associare un utente a ogni chiamata.

Di seguito è riportato un esempio di Program.cs che utilizza l'autenticazione gRPC e ASP.NET Core:

app.UseRouting();

app.UseAuthentication();
app.UseAuthorization();

app.MapGrpcService<GreeterService>();

Note

L'ordine in cui registrare il middleware di autenticazione principale di ASP.NET è importante. Chiamare sempre UseAuthentication e UseAuthorization dopo UseRouting e prima di UseEndpoints.

Il meccanismo di autenticazione usato dall'app durante una chiamata deve essere configurato. La configurazione di autenticazione viene aggiunta in Program.cs e sarà diversa a seconda del meccanismo di autenticazione usato dall'app.

Dopo aver configurato l'autenticazione, l'utente può accedere ai metodi del servizio gRPC tramite .ServerCallContext

public override Task<BuyTicketsResponse> BuyTickets(
    BuyTicketsRequest request, ServerCallContext context)
{
    var user = context.GetHttpContext().User;

    // ... access data from ClaimsPrincipal ...
}

Autenticazione del token portatore

Il client può fornire un token di accesso per l'autenticazione. Il server convalida il token e lo usa per identificare l'utente.

Nel server, l'autenticazione tramite token bearer è configurata usando il middleware JWT Bearer.

Nel client .NET gRPC, il token può essere inviato nelle chiamate usando la Metadata raccolta. Gli elementi nella Metadata raccolta vengono inviati con una chiamata gRPC come intestazioni HTTP.

public bool DoAuthenticatedCall(
    Ticketer.TicketerClient client, string token)
{
    var headers = new Metadata();
    headers.Add("Authorization", $"Bearer {token}");

    var request = new BuyTicketsRequest { Count = 1 };
    var response = await client.BuyTicketsAsync(request, headers);

    return response.Success;
}

Impostare il token di autorizzazione con CallCredentials

Configurare ChannelCredentials su un canale è un modo alternativo per inviare il token al servizio con chiamate gRPC. Un ChannelCredentials può includere CallCredentials, che consente di impostare Metadata automaticamente.

Vantaggi dell'uso CallCredentialsdi :

  • L'autenticazione viene configurata centralmente nel canale. Il token non deve essere fornito manualmente alla chiamata gRPC.
  • Il CallCredentials.FromInterceptor callback è asincrono. Le credenziali di chiamata possono recuperare un token di credenziali da un sistema esterno, se necessario. I metodi asincroni all'interno del callback dovrebbero utilizzare CancellationToken su AuthInterceptorContext.

Note

CallCredentials vengono applicati solo se il canale è protetto con TLS. L'invio di intestazioni di autenticazione tramite una connessione non sicura ha implicazioni per la sicurezza e non deve essere eseguita negli ambienti di produzione. Un'app può configurare un canale per ignorare questo comportamento e utilizzare sempre CallCredentials impostando UnsafeUseInsecureChannelCallCredentials su un canale.

La credenziale nell'esempio seguente configura il canale per inviare il token con ogni chiamata gRPC:

private static GrpcChannel CreateAuthenticatedChannel(ITokenProvder tokenProvider)
{
    var credentials = CallCredentials.FromInterceptor(async (context, metadata) =>
    {
        var token = await tokenProvider.GetTokenAsync(context.CancellationToken);
        metadata.Add("Authorization", $"Bearer {token}");
    });

    var channel = GrpcChannel.ForAddress(address, new GrpcChannelOptions
    {
        Credentials = ChannelCredentials.Create(new SslCredentials(), credentials)
    });
    return channel;
}

Token portatore con factory di client gRPC

La fabbrica client gRPC può creare dei client che inviano un token di tipo bearer usando AddCallCredentials. Questo metodo è disponibile in Grpc.Net.ClientFactory versione 2.46.0 o successiva.

Il delegato passato a AddCallCredentials viene eseguito per ogni chiamata gRPC:

builder.Services
    .AddGrpcClient<Greeter.GreeterClient>(o =>
    {
        o.Address = new Uri("https://localhost:5001");
    })
    .AddCallCredentials((context, metadata) =>
    {
        if (!string.IsNullOrEmpty(_token))
        {
            metadata.Add("Authorization", $"Bearer {_token}");
        }
        return Task.CompletedTask;
    });

L'iniezione delle dipendenze può essere combinata con AddCallCredentials. Un overload passa IServiceProvider al delegato, che può essere utilizzato per ottenere un servizio costruito dall'inserimento di dipendenze utilizzando servizi a ambito variabile e temporanei.

Si consideri un'app con:

  • Definito dall'utente ITokenProvider per ottenere un token di accesso. ITokenProvider è registrato nell'inserimento di dipendenze con una durata con ambito.
  • La factory client gRPC è configurata per creare client che vengono iniettati nei servizi gRPC e nei controller API Web.
  • Le chiamate gRPC devono usare ITokenProvider per ottenere un token di connessione.
public interface ITokenProvider
{
    Task<string> GetTokenAsync(CancellationToken cancellationToken);
}

public class AppTokenProvider : ITokenProvider
{
    private string _token;

    public async Task<string> GetTokenAsync(CancellationToken cancellationToken)
    {
        if (_token == null)
        {
            // App code to resolve the token here.
        }

        return _token;
    }
}
builder.Services.AddScoped<ITokenProvider, AppTokenProvider>();

builder.Services
    .AddGrpcClient<Greeter.GreeterClient>(o =>
    {
        o.Address = new Uri("https://localhost:5001");
    })
    .AddCallCredentials(async (context, metadata, serviceProvider) =>
    {
        var provider = serviceProvider.GetRequiredService<ITokenProvider>();
        var token = await provider.GetTokenAsync(context.CancellationToken);
        metadata.Add("Authorization", $"Bearer {token}");
    }));

Il codice precedente:

  • Definisce ITokenProvider e AppTokenProvider. Questi tipi gestiscono la risoluzione del token di autenticazione per le chiamate gRPC.
  • Registra il tipo AppTokenProvider con l'iniezione delle dipendenze (ID) in una durata limitata. AppTokenProvider memorizza nella cache il token in modo che sia necessaria solo la prima chiamata nell'ambito per calcolarla.
  • Registra il tipo GreeterClient con il client factory.
  • Configura AddCallCredentials per questo client. Il delegato viene eseguito ogni volta che viene effettuata una chiamata e aggiunge il token restituito da ITokenProvider ai metadati.

Autenticazione con certificato client

Un client potrebbe in alternativa fornire un certificato client per l'autenticazione. L'autenticazione del certificato avviene a livello di TLS, molto prima che arrivi a ASP.NET Core. Quando la richiesta entra in ASP.NET Core, il pacchetto di autenticazione del certificato client consente di risolvere il certificato in un oggetto ClaimsPrincipal.

Note

Configurare il server per accettare i certificati client. Per informazioni sull'accettazione dei certificati client in Kestrel, IIS e Azure, vedere Configurare l'autenticazione del certificato in ASP.NET Core.

Nel client .NET gRPC il certificato client viene aggiunto a HttpClientHandler che viene quindi usato per creare il client gRPC:

public Ticketer.TicketerClient CreateClientWithCert(
    string baseAddress,
    X509Certificate2 certificate)
{
    // Add client cert to the handler
    var handler = new HttpClientHandler();
    handler.ClientCertificates.Add(certificate);

    // Create the gRPC channel
    var channel = GrpcChannel.ForAddress(baseAddress, new GrpcChannelOptions
    {
        HttpHandler = handler
    });

    return new Ticketer.TicketerClient(channel);
}

Altri meccanismi di autenticazione

Molti meccanismi di autenticazione supportati da ASP.NET Core funzionano con gRPC:

  • Microsoft Entra ID
  • Certificato del cliente
  • IdentityServer
  • JWT Token
  • OAuth 2.0
  • OpenID Connect
  • WS-Federation

Per altre informazioni sulla configurazione dell'autenticazione nel server, vedere autenticazione di ASP.NET Core.

La configurazione del client gRPC per l'uso dell'autenticazione dipenderà dal meccanismo di autenticazione in uso. Gli esempi precedenti di token di connessione e certificato client mostrano alcuni modi in cui il client gRPC può essere configurato per inviare i metadati di autenticazione con chiamate gRPC:

  • I client gRPC con tipizzazione forte usano HttpClient internamente. L'autenticazione può essere configurata in HttpClientHandlero aggiungendo istanze personalizzate HttpMessageHandler a HttpClient.
  • Ogni chiamata gRPC ha un argomento facoltativo CallOptions . È possibile inviare intestazioni personalizzate usando l'insieme di intestazioni dell'opzione.

Note

L'autenticazione di Windows (NTLM/Kerberos/Negotiate) non può essere usata con gRPC. gRPC richiede HTTP/2 e HTTP/2 non supporta l'autenticazione di Windows.

Autorizzare gli utenti ad accedere ai servizi e ai metodi di servizio

Per impostazione predefinita, tutti i metodi in un servizio possono essere chiamati da utenti non autenticati. Per richiedere l'autenticazione, applicare l'attributo [Authorize] al servizio:

[Authorize]
public class TicketerService : Ticketer.TicketerBase
{
}

È possibile usare gli argomenti e le proprietà del costruttore dell'attributo per limitare l'accesso [Authorize] solo agli utenti che corrispondono a criteri di autorizzazione specifici. Ad esempio, se si dispone di un criterio di autorizzazione personalizzato denominato MyAuthorizationPolicy, assicurarsi che solo gli utenti corrispondenti a tale criterio possano accedere al servizio usando il codice seguente:

[Authorize("MyAuthorizationPolicy")]
public class TicketerService : Ticketer.TicketerBase
{
}

I singoli metodi del servizio possono avere anche l'attributo [Authorize] applicato. Se l'utente corrente non corrisponde ai criteri applicati sia al metodo che alla classe , viene restituito un errore al chiamante:

[Authorize]
public class TicketerService : Ticketer.TicketerBase
{
    public override Task<AvailableTicketsResponse> GetAvailableTickets(
        Empty request, ServerCallContext context)
    {
        // ... buy tickets for the current user ...
    }

    [Authorize("Administrators")]
    public override Task<BuyTicketsResponse> RefundTickets(
        BuyTicketsRequest request, ServerCallContext context)
    {
        // ... refund tickets (something only Administrators can do) ..
    }
}

Risorse aggiuntive

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

Autenticare gli utenti che chiamano un servizio gRPC

gRPC può essere usato con ASP.NET Core authentication per associare un utente a ogni chiamata.

Di seguito è riportato un esempio di Startup.Configure che utilizza l'autenticazione gRPC e ASP.NET Core:

public void Configure(IApplicationBuilder app)
{
    app.UseRouting();

    app.UseAuthentication();
    app.UseAuthorization();

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapGrpcService<GreeterService>();
    });
}

Note

L'ordine in cui registrare il middleware di autenticazione principale di ASP.NET è importante. Chiamare sempre UseAuthentication e UseAuthorization dopo UseRouting e prima di UseEndpoints.

Il meccanismo di autenticazione usato dall'app durante una chiamata deve essere configurato. La configurazione di autenticazione viene aggiunta in Startup.ConfigureServices e sarà diversa a seconda del meccanismo di autenticazione usato dall'app.

Dopo aver configurato l'autenticazione, l'utente può accedere ai metodi del servizio gRPC tramite .ServerCallContext

public override Task<BuyTicketsResponse> BuyTickets(
    BuyTicketsRequest request, ServerCallContext context)
{
    var user = context.GetHttpContext().User;

    // ... access data from ClaimsPrincipal ...
}

Autenticazione del token portatore

Il client può fornire un token di accesso per l'autenticazione. Il server convalida il token e lo usa per identificare l'utente.

Nel server, l'autenticazione tramite token bearer è configurata usando il middleware JWT Bearer.

Nel client .NET gRPC, il token può essere inviato nelle chiamate usando la Metadata raccolta. Gli elementi nella Metadata raccolta vengono inviati con una chiamata gRPC come intestazioni HTTP.

public bool DoAuthenticatedCall(
    Ticketer.TicketerClient client, string token)
{
    var headers = new Metadata();
    headers.Add("Authorization", $"Bearer {token}");

    var request = new BuyTicketsRequest { Count = 1 };
    var response = await client.BuyTicketsAsync(request, headers);

    return response.Success;
}

Impostare il token di autorizzazione con CallCredentials

Configurare ChannelCredentials su un canale è un modo alternativo per inviare il token al servizio con chiamate gRPC. Un ChannelCredentials può includere CallCredentials, che consente di impostare Metadata automaticamente.

Vantaggi dell'uso CallCredentialsdi :

  • L'autenticazione viene configurata centralmente nel canale. Il token non deve essere fornito manualmente alla chiamata gRPC.
  • Il CallCredentials.FromInterceptor callback è asincrono. Le credenziali di chiamata possono recuperare un token di credenziali da un sistema esterno, se necessario. I metodi asincroni all'interno del callback dovrebbero utilizzare CancellationToken su AuthInterceptorContext.

Note

CallCredentials vengono applicati solo se il canale è protetto con TLS. L'invio di intestazioni di autenticazione tramite una connessione non sicura ha implicazioni per la sicurezza e non deve essere eseguita negli ambienti di produzione. Un'app può configurare un canale per ignorare questo comportamento e utilizzare sempre CallCredentials impostando UnsafeUseInsecureChannelCallCredentials su un canale.

La credenziale nell'esempio seguente configura il canale per inviare il token con ogni chiamata gRPC:

private static GrpcChannel CreateAuthenticatedChannel(ITokenProvder tokenProvider)
{
    var credentials = CallCredentials.FromInterceptor(async (context, metadata) =>
    {
        var token = await tokenProvider.GetTokenAsync(context.CancellationToken);
        metadata.Add("Authorization", $"Bearer {token}");
    });

    var channel = GrpcChannel.ForAddress(address, new GrpcChannelOptions
    {
        Credentials = ChannelCredentials.Create(new SslCredentials(), credentials)
    });
    return channel;
}

Token portatore con factory di client gRPC

La fabbrica client gRPC può creare dei client che inviano un token di tipo bearer usando AddCallCredentials. Questo metodo è disponibile in Grpc.Net.ClientFactory versione 2.46.0 o successiva.

Il delegato passato a AddCallCredentials viene eseguito per ogni chiamata gRPC:

services
    .AddGrpcClient<Greeter.GreeterClient>(o =>
    {
        o.Address = new Uri("https://localhost:5001");
    })
    .AddCallCredentials((context, metadata) =>
    {
        if (!string.IsNullOrEmpty(_token))
        {
            metadata.Add("Authorization", $"Bearer {_token}");
        }
        return Task.CompletedTask;
    });

L'iniezione delle dipendenze può essere combinata con AddCallCredentials. Un overload passa IServiceProvider al delegato, che può essere utilizzato per ottenere un servizio costruito dall'inserimento di dipendenze utilizzando servizi a ambito variabile e temporanei.

Si consideri un'app con:

  • Definito dall'utente ITokenProvider per ottenere un token di accesso. ITokenProvider è registrato nell'inserimento di dipendenze con una durata con ambito.
  • La factory client gRPC è configurata per creare client che vengono iniettati nei servizi gRPC e nei controller API Web.
  • Le chiamate gRPC devono usare ITokenProvider per ottenere un token di connessione.
public interface ITokenProvider
{
    Task<string> GetTokenAsync(CancellationToken cancellationToken);
}

public class AppTokenProvider : ITokenProvider
{
    private string _token;

    public async Task<string> GetTokenAsync(CancellationToken cancellationToken)
    {
        if (_token == null)
        {
            // App code to resolve the token here.
        }

        return _token;
    }
}
services.AddScoped<ITokenProvider, AppTokenProvider>();

services
    .AddGrpcClient<Greeter.GreeterClient>(o =>
    {
        o.Address = new Uri("https://localhost:5001");
    })
    .AddCallCredentials(async (context, metadata, serviceProvider) =>
    {
        var provider = serviceProvider.GetRequiredService<ITokenProvider>();
        var token = await provider.GetTokenAsync(context.CancellationToken);
        metadata.Add("Authorization", $"Bearer {token}");
    }));

Il codice precedente:

  • Definisce ITokenProvider e AppTokenProvider. Questi tipi gestiscono la risoluzione del token di autenticazione per le chiamate gRPC.
  • Registra il tipo AppTokenProvider con l'iniezione delle dipendenze (ID) in una durata limitata. AppTokenProvider memorizza nella cache il token in modo che sia necessaria solo la prima chiamata nell'ambito per calcolarla.
  • Registra il tipo GreeterClient con il client factory.
  • Configura AddCallCredentials per questo client. Il delegato viene eseguito ogni volta che viene effettuata una chiamata e aggiunge il token restituito da ITokenProvider ai metadati.

Autenticazione con certificato client

Un client potrebbe in alternativa fornire un certificato client per l'autenticazione. L'autenticazione del certificato avviene a livello di TLS, molto prima che arrivi a ASP.NET Core. Quando la richiesta entra in ASP.NET Core, il pacchetto di autenticazione del certificato client consente di risolvere il certificato in un oggetto ClaimsPrincipal.

Note

Configurare il server per accettare i certificati client. Per informazioni sull'accettazione dei certificati client in Kestrel, IIS e Azure, vedere Configurare l'autenticazione del certificato in ASP.NET Core.

Nel client .NET gRPC il certificato client viene aggiunto a HttpClientHandler che viene quindi usato per creare il client gRPC:

public Ticketer.TicketerClient CreateClientWithCert(
    string baseAddress,
    X509Certificate2 certificate)
{
    // Add client cert to the handler
    var handler = new HttpClientHandler();
    handler.ClientCertificates.Add(certificate);

    // Create the gRPC channel
    var channel = GrpcChannel.ForAddress(baseAddress, new GrpcChannelOptions
    {
        HttpHandler = handler
    });

    return new Ticketer.TicketerClient(channel);
}

Altri meccanismi di autenticazione

Molti meccanismi di autenticazione supportati da ASP.NET Core funzionano con gRPC:

  • Microsoft Entra ID
  • Certificato del cliente
  • IdentityServer
  • JWT Token
  • OAuth 2.0
  • OpenID Connect
  • WS-Federation

Per altre informazioni sulla configurazione dell'autenticazione nel server, vedere autenticazione di ASP.NET Core.

La configurazione del client gRPC per l'uso dell'autenticazione dipenderà dal meccanismo di autenticazione in uso. Gli esempi precedenti di token di connessione e certificato client mostrano alcuni modi in cui il client gRPC può essere configurato per inviare i metadati di autenticazione con chiamate gRPC:

  • I client gRPC con tipizzazione forte usano HttpClient internamente. L'autenticazione può essere configurata in HttpClientHandlero aggiungendo istanze personalizzate HttpMessageHandler a HttpClient.
  • Ogni chiamata gRPC ha un argomento facoltativo CallOptions . È possibile inviare intestazioni personalizzate usando l'insieme di intestazioni dell'opzione.

Note

L'autenticazione di Windows (NTLM/Kerberos/Negotiate) non può essere usata con gRPC. gRPC richiede HTTP/2 e HTTP/2 non supporta l'autenticazione di Windows.

Autorizzare gli utenti ad accedere ai servizi e ai metodi di servizio

Per impostazione predefinita, tutti i metodi in un servizio possono essere chiamati da utenti non autenticati. Per richiedere l'autenticazione, applicare l'attributo [Authorize] al servizio:

[Authorize]
public class TicketerService : Ticketer.TicketerBase
{
}

È possibile usare gli argomenti e le proprietà del costruttore dell'attributo per limitare l'accesso [Authorize] solo agli utenti che corrispondono a criteri di autorizzazione specifici. Ad esempio, se si dispone di un criterio di autorizzazione personalizzato denominato MyAuthorizationPolicy, assicurarsi che solo gli utenti corrispondenti a tale criterio possano accedere al servizio usando il codice seguente:

[Authorize("MyAuthorizationPolicy")]
public class TicketerService : Ticketer.TicketerBase
{
}

I singoli metodi del servizio possono avere anche l'attributo [Authorize] applicato. Se l'utente corrente non corrisponde ai criteri applicati sia al metodo che alla classe , viene restituito un errore al chiamante:

[Authorize]
public class TicketerService : Ticketer.TicketerBase
{
    public override Task<AvailableTicketsResponse> GetAvailableTickets(
        Empty request, ServerCallContext context)
    {
        // ... buy tickets for the current user ...
    }

    [Authorize("Administrators")]
    public override Task<BuyTicketsResponse> RefundTickets(
        BuyTicketsRequest request, ServerCallContext context)
    {
        // ... refund tickets (something only Administrators can do) ..
    }
}

Metodi di estensione dell'autorizzazione

Authorizaton può anche essere controllato usando metodi di estensione dell'autorizzazione standard ASP.NET Core, ad esempio AllowAnonymous e RequireAuthorization.

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddGrpc();

var app = builder.Build();
app.MapGrpcService<TicketerService>().RequireAuthorization("Administrators");
app.Run();

Risorse aggiuntive