Share via


Autenticazione e autorizzazione in gRPC per ASP.NET Core

Di James Newton-King

Visualizzare o scaricare il codice di esempio (come scaricare)

Autenticare gli utenti che chiamano un servizio gRPC

GRPC può essere usato con ASP.NET'autenticazione core per associare un utente a ogni chiamata.

Di seguito è riportato un esempio di utilizzo di Program.cs gRPC e ASP.NET'autenticazione core:

app.UseRouting();

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

app.MapGrpcService<GreeterService>();

Nota

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

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 di connessione

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 del token di connessione viene configurata usando il middleware bearer JWT.

Nel client .NET gRPC il token può essere inviato con chiamate usando la Metadata raccolta. Le voci nella Metadata raccolta vengono inviate 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 connessione con CallCredentials

La configurazione ChannelCredentials in un canale è un modo alternativo per inviare il token al servizio con chiamate gRPC. Un ChannelCredentials oggetto può includere CallCredentials, che consentono di impostare Metadataautomaticamente .

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 devono usare in CancellationTokenAuthInterceptorContext.

Nota

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 usarlo CallCredentials sempre 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 di connessione con la factory client gRPC

La factory client gRPC può creare client che inviano un token di connessione 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'inserimento delle dipendenze può essere combinato con AddCallCredentials. Un overload passa IServiceProvider al delegato, che può essere usato per ottenere un servizio costruito dall'inserimento di dipendenze tramite servizi temporanei e con ambito.

Si consideri un'app con:

  • Definito dall'utente ITokenProvider per ottenere un token di connessione. ITokenProvider è registrato nell'inserimento di dipendenze con una durata con ambito.
  • La factory client gRPC è configurata per creare client inseriti in servizi gRPC e 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 con l'inserimento AppTokenProvider delle dipendenze in una durata con ambito. AppTokenProvider memorizza nella cache il token in modo che sia necessaria solo la prima chiamata nell'ambito per calcolarla.
  • Registra il GreeterClient tipo con la factory client.
  • AddCallCredentials Configura per questo client. Il delegato viene eseguito ogni volta che viene effettuata una chiamata e aggiunge il token restituito dai ITokenProvider 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 immette ASP.NET Core, il pacchetto di autenticazione del certificato client consente di risolvere il certificato in un oggetto ClaimsPrincipal.

Nota

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 client
  • 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 fortemente tipizzato 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.

Nota

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 (come scaricare)

Autenticare gli utenti che chiamano un servizio gRPC

GRPC può essere usato con ASP.NET'autenticazione core per associare un utente a ogni chiamata.

Di seguito è riportato un esempio di utilizzo di Startup.Configure gRPC e ASP.NET'autenticazione core:

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

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

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

Nota

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

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 di connessione

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 del token di connessione viene configurata usando il middleware bearer JWT.

Nel client .NET gRPC il token può essere inviato con chiamate usando la Metadata raccolta. Le voci nella Metadata raccolta vengono inviate 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 connessione con CallCredentials

La configurazione ChannelCredentials in un canale è un modo alternativo per inviare il token al servizio con chiamate gRPC. Un ChannelCredentials oggetto può includere CallCredentials, che consentono di impostare Metadataautomaticamente .

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 devono usare in CancellationTokenAuthInterceptorContext.

Nota

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 usarlo CallCredentials sempre 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 di connessione con la factory client gRPC

La factory client gRPC può creare client che inviano un token di connessione 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'inserimento delle dipendenze può essere combinato con AddCallCredentials. Un overload passa IServiceProvider al delegato, che può essere usato per ottenere un servizio costruito dall'inserimento di dipendenze tramite servizi temporanei e con ambito.

Si consideri un'app con:

  • Definito dall'utente ITokenProvider per ottenere un token di connessione. ITokenProvider è registrato nell'inserimento di dipendenze con una durata con ambito.
  • La factory client gRPC è configurata per creare client inseriti in servizi gRPC e 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 con l'inserimento AppTokenProvider delle dipendenze in una durata con ambito. AppTokenProvider memorizza nella cache il token in modo che sia necessaria solo la prima chiamata nell'ambito per calcolarla.
  • Registra il GreeterClient tipo con la factory client.
  • AddCallCredentials Configura per questo client. Il delegato viene eseguito ogni volta che viene effettuata una chiamata e aggiunge il token restituito dai ITokenProvider 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 immette ASP.NET Core, il pacchetto di autenticazione del certificato client consente di risolvere il certificato in un oggetto ClaimsPrincipal.

Nota

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 client
  • 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 fortemente tipizzato 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.

Nota

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