Uwierzytelnianie i autoryzacja w gRPC dla ASP.NET Core

Autor: James Newton-King

Wyświetlanie lub pobieranie przykładowego kodu (jak pobrać)

Uwierzytelnianie użytkowników wywołujących usługę gRPC

GRPC można używać z uwierzytelnianiem ASP.NET Core w celu skojarzenia użytkownika z każdym wywołaniem.

Poniżej przedstawiono przykład użycia Program.cs uwierzytelniania gRPC i ASP.NET Core:

app.UseRouting();

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

app.MapGrpcService<GreeterService>();

Uwaga

Kolejność rejestrowania oprogramowania pośredniczącego ASP.NET Core ma znaczenie. Zawsze dzwonij UseAuthentication i UseAuthorization po UseRouting i przed UseEndpoints.

Mechanizm uwierzytelniania używany przez aplikację podczas wywołania musi być skonfigurowany. Konfiguracja uwierzytelniania jest dodawana i Program.cs będzie się różnić w zależności od mechanizmu uwierzytelniania używanego przez aplikację.

Po skonfigurowaniu uwierzytelniania można uzyskać dostęp do użytkownika w metodach usługi gRPC za pośrednictwem .ServerCallContext

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

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

Uwierzytelnianie tokenu elementu nośnego

Klient może zapewnić token dostępu do uwierzytelniania. Serwer weryfikuje token i używa go do identyfikowania użytkownika.

Na serwerze uwierzytelnianie tokenu elementu nośnego jest konfigurowane przy użyciu oprogramowania pośredniczącego elementu nośnego JWT.

W kliencie gRPC platformy .NET token można wysyłać z wywołaniami przy użyciu kolekcji Metadata . Wpisy w Metadata kolekcji są wysyłane za pomocą wywołania gRPC jako nagłówków 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;
}

Ustawianie tokenu elementu nośnego za pomocą polecenia CallCredentials

Konfigurowanie ChannelCredentials w kanale jest alternatywnym sposobem wysyłania tokenu do usługi za pomocą wywołań gRPC. Element ChannelCredentials może zawierać CallCredentialselement , który umożliwia automatyczne ustawianie Metadataelementu .

Zalety korzystania z programu CallCredentials:

  • Uwierzytelnianie jest konfigurowane centralnie w kanale. Token nie musi być udostępniany ręcznie wywołaniu gRPC.
  • Wywołanie CallCredentials.FromInterceptor zwrotne jest asynchroniczne. W razie potrzeby poświadczenia wywołania mogą pobierać token poświadczeń z systemu zewnętrznego. Metody asynchroniczne wewnątrz wywołania zwrotnego powinny używać metody CancellationToken na .AuthInterceptorContext

Uwaga

CallCredentials są stosowane tylko wtedy, gdy kanał jest zabezpieczony przy użyciu protokołu TLS. Wysyłanie nagłówków uwierzytelniania za pośrednictwem niezabezpieczonego połączenia ma wpływ na bezpieczeństwo i nie powinno być wykonywane w środowiskach produkcyjnych. Aplikacja może skonfigurować kanał tak, aby ignorował to zachowanie i zawsze używać CallCredentials go przez ustawienie UnsafeUseInsecureChannelCallCredentials w kanale.

Poświadczenie w poniższym przykładzie konfiguruje kanał do wysyłania tokenu z każdym wywołaniem 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 elementu nośnego z fabryką klienta gRPC

Fabryka klienta gRPC może tworzyć klientów, którzy wysyłają token elementu nośnego przy użyciu polecenia AddCallCredentials. Ta metoda jest dostępna w grpc.Net.ClientFactory w wersji 2.46.0 lub nowszej.

Delegat przekazany do AddCallCredentials jest wykonywany dla każdego wywołania 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;
    });

Wstrzykiwanie zależności (DI) można połączyć z AddCallCredentials. Przeciążenie przechodzi IServiceProvider do delegata, który może służyć do pobierania usługi skonstruowanej z di przy użyciu usług o określonym zakresie i przejściowych.

Rozważ aplikację, która ma następujące kwestie:

  • Zdefiniowana przez ITokenProvider użytkownika na potrzeby uzyskiwania tokenu elementu nośnego. ITokenProvider jest rejestrowany w di z okresem istnienia o określonym zakresie.
  • Fabryka klienta gRPC jest skonfigurowana do tworzenia klientów, którzy są wstrzykiwani do usług gRPC i kontrolerów internetowego interfejsu API.
  • Wywołania gRPC powinny służyć ITokenProvider do uzyskania tokenu elementu nośnego.
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}");
    }));

Powyższy kod ma następujące działanie:

  • Definiuje ITokenProvider i AppTokenProvider. Te typy obsługują rozpoznawanie tokenu uwierzytelniania dla wywołań gRPC.
  • Rejestruje AppTokenProvider typ z di w okresie istnienia o określonym zakresie. AppTokenProvider Buforuje token, tak aby tylko pierwsze wywołanie w zakresie było wymagane do jego obliczenia.
  • Rejestruje typ w GreeterClient fabryce klienta.
  • Konfiguruje AddCallCredentials dla tego klienta. Delegat jest wykonywany za każdym razem, gdy jest wykonywane wywołanie i dodaje token zwracany przez ITokenProvider element do metadanych.

Uwierzytelnianie certyfikatów klientów

Klient może również podać certyfikat klienta na potrzeby uwierzytelniania. Uwierzytelnianie certyfikatu odbywa się na poziomie protokołu TLS, na długo przed rozpoczęciem ASP.NET Core. Po wprowadzeniu żądania ASP.NET Core pakiet uwierzytelniania certyfikatu klienta umożliwia rozpoznawanie certyfikatu na ClaimsPrincipal.

Uwaga

Skonfiguruj serwer tak, aby akceptował certyfikaty klienta. Aby uzyskać informacje na temat akceptowania certyfikatów klienta w Kestrelusługach IIS i na platformie Azure, zobacz Konfigurowanie uwierzytelniania certyfikatów w programie ASP.NET Core.

W kliencie gRPC platformy .NET certyfikat klienta jest dodawany do HttpClientHandler tego certyfikatu, a następnie używany do tworzenia klienta 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);
}

Inne mechanizmy uwierzytelniania

Wiele ASP.NET Core obsługiwanych mechanizmów uwierzytelniania działa z gRPC:

  • Microsoft Entra ID
  • Certyfikat klienta
  • IdentitySerwera
  • JWT Token
  • OAuth 2.0
  • OpenID Connect
  • WS-Federation

Aby uzyskać więcej informacji na temat konfigurowania uwierzytelniania na serwerze, zobacz ASP.NET Core authentication (Uwierzytelnianie podstawowe ASP.NET).

Skonfigurowanie klienta gRPC do korzystania z uwierzytelniania będzie zależeć od używanego mechanizmu uwierzytelniania. Poprzednie przykłady tokenu elementu nośnego i certyfikatu klienta pokazują kilka sposobów konfigurowania klienta gRPC do wysyłania metadanych uwierzytelniania za pomocą wywołań gRPC:

  • Silnie typizowane klienci gRPC używają HttpClient wewnętrznie. Uwierzytelnianie można skonfigurować w systemie HttpClientHandlerlub przez dodanie wystąpień niestandardowych HttpMessageHandler do elementu HttpClient.
  • Każde wywołanie gRPC ma opcjonalny CallOptions argument. Nagłówki niestandardowe można wysyłać przy użyciu kolekcji nagłówków opcji.

Uwaga

Uwierzytelnianie systemu Windows (NTLM/Kerberos/Negotiate) nie może być używane z gRPC. Usługa gRPC wymaga protokołu HTTP/2, a protokół HTTP/2 nie obsługuje uwierzytelniania systemu Windows.

Autoryzowanie użytkowników do uzyskiwania dostępu do usług i metod usług

Domyślnie wszystkie metody w usłudze mogą być wywoływane przez nieuwierzytelnionych użytkowników. Aby wymagać uwierzytelniania, zastosuj [Authorize] atrybut do usługi:

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

Można użyć argumentów konstruktora i właściwości atrybutu [Authorize] , aby ograniczyć dostęp tylko do użytkowników pasujących do określonych zasad autoryzacji. Jeśli na przykład masz niestandardowe zasady autoryzacji o nazwie MyAuthorizationPolicy, upewnij się, że tylko użytkownicy pasujący do tych zasad mogą uzyskiwać dostęp do usługi przy użyciu następującego kodu:

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

Poszczególne metody usług mogą również mieć [Authorize] zastosowany atrybut. Jeśli bieżący użytkownik nie jest zgodny z zasadami zastosowanymi zarówno do metody, jak i klasy, zostanie zwrócony błąd do obiektu wywołującego:

[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) ..
    }
}

Dodatkowe zasoby

Wyświetlanie lub pobieranie przykładowego kodu (jak pobrać)

Uwierzytelnianie użytkowników wywołujących usługę gRPC

GRPC można używać z uwierzytelnianiem ASP.NET Core w celu skojarzenia użytkownika z każdym wywołaniem.

Poniżej przedstawiono przykład użycia Startup.Configure uwierzytelniania gRPC i ASP.NET Core:

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

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

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

Uwaga

Kolejność rejestrowania oprogramowania pośredniczącego ASP.NET Core ma znaczenie. Zawsze dzwonij UseAuthentication i UseAuthorization po UseRouting i przed UseEndpoints.

Mechanizm uwierzytelniania używany przez aplikację podczas wywołania musi być skonfigurowany. Konfiguracja uwierzytelniania jest dodawana i Startup.ConfigureServices będzie się różnić w zależności od mechanizmu uwierzytelniania używanego przez aplikację.

Po skonfigurowaniu uwierzytelniania można uzyskać dostęp do użytkownika w metodach usługi gRPC za pośrednictwem .ServerCallContext

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

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

Uwierzytelnianie tokenu elementu nośnego

Klient może zapewnić token dostępu do uwierzytelniania. Serwer weryfikuje token i używa go do identyfikowania użytkownika.

Na serwerze uwierzytelnianie tokenu elementu nośnego jest konfigurowane przy użyciu oprogramowania pośredniczącego elementu nośnego JWT.

W kliencie gRPC platformy .NET token można wysyłać z wywołaniami przy użyciu kolekcji Metadata . Wpisy w Metadata kolekcji są wysyłane za pomocą wywołania gRPC jako nagłówków 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;
}

Ustawianie tokenu elementu nośnego za pomocą polecenia CallCredentials

Konfigurowanie ChannelCredentials w kanale jest alternatywnym sposobem wysyłania tokenu do usługi za pomocą wywołań gRPC. Element ChannelCredentials może zawierać CallCredentialselement , który umożliwia automatyczne ustawianie Metadataelementu .

Zalety korzystania z programu CallCredentials:

  • Uwierzytelnianie jest konfigurowane centralnie w kanale. Token nie musi być udostępniany ręcznie wywołaniu gRPC.
  • Wywołanie CallCredentials.FromInterceptor zwrotne jest asynchroniczne. W razie potrzeby poświadczenia wywołania mogą pobierać token poświadczeń z systemu zewnętrznego. Metody asynchroniczne wewnątrz wywołania zwrotnego powinny używać metody CancellationToken na .AuthInterceptorContext

Uwaga

CallCredentials są stosowane tylko wtedy, gdy kanał jest zabezpieczony przy użyciu protokołu TLS. Wysyłanie nagłówków uwierzytelniania za pośrednictwem niezabezpieczonego połączenia ma wpływ na bezpieczeństwo i nie powinno być wykonywane w środowiskach produkcyjnych. Aplikacja może skonfigurować kanał tak, aby ignorował to zachowanie i zawsze używać CallCredentials go przez ustawienie UnsafeUseInsecureChannelCallCredentials w kanale.

Poświadczenie w poniższym przykładzie konfiguruje kanał do wysyłania tokenu z każdym wywołaniem 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 elementu nośnego z fabryką klienta gRPC

Fabryka klienta gRPC może tworzyć klientów, którzy wysyłają token elementu nośnego przy użyciu polecenia AddCallCredentials. Ta metoda jest dostępna w grpc.Net.ClientFactory w wersji 2.46.0 lub nowszej.

Delegat przekazany do AddCallCredentials jest wykonywany dla każdego wywołania 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;
    });

Wstrzykiwanie zależności (DI) można połączyć z AddCallCredentials. Przeciążenie przechodzi IServiceProvider do delegata, który może służyć do pobierania usługi skonstruowanej z di przy użyciu usług o określonym zakresie i przejściowych.

Rozważ aplikację, która ma następujące kwestie:

  • Zdefiniowana przez ITokenProvider użytkownika na potrzeby uzyskiwania tokenu elementu nośnego. ITokenProvider jest rejestrowany w di z okresem istnienia o określonym zakresie.
  • Fabryka klienta gRPC jest skonfigurowana do tworzenia klientów, którzy są wstrzykiwani do usług gRPC i kontrolerów internetowego interfejsu API.
  • Wywołania gRPC powinny służyć ITokenProvider do uzyskania tokenu elementu nośnego.
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}");
    }));

Powyższy kod ma następujące działanie:

  • Definiuje ITokenProvider i AppTokenProvider. Te typy obsługują rozpoznawanie tokenu uwierzytelniania dla wywołań gRPC.
  • Rejestruje AppTokenProvider typ z di w okresie istnienia o określonym zakresie. AppTokenProvider Buforuje token, tak aby tylko pierwsze wywołanie w zakresie było wymagane do jego obliczenia.
  • Rejestruje typ w GreeterClient fabryce klienta.
  • Konfiguruje AddCallCredentials dla tego klienta. Delegat jest wykonywany za każdym razem, gdy jest wykonywane wywołanie i dodaje token zwracany przez ITokenProvider element do metadanych.

Uwierzytelnianie certyfikatów klientów

Klient może również podać certyfikat klienta na potrzeby uwierzytelniania. Uwierzytelnianie certyfikatu odbywa się na poziomie protokołu TLS, na długo przed rozpoczęciem ASP.NET Core. Po wprowadzeniu żądania ASP.NET Core pakiet uwierzytelniania certyfikatu klienta umożliwia rozpoznawanie certyfikatu na ClaimsPrincipal.

Uwaga

Skonfiguruj serwer tak, aby akceptował certyfikaty klienta. Aby uzyskać informacje na temat akceptowania certyfikatów klienta w Kestrelusługach IIS i na platformie Azure, zobacz Konfigurowanie uwierzytelniania certyfikatów w programie ASP.NET Core.

W kliencie gRPC platformy .NET certyfikat klienta jest dodawany do HttpClientHandler tego certyfikatu, a następnie używany do tworzenia klienta 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);
}

Inne mechanizmy uwierzytelniania

Wiele ASP.NET Core obsługiwanych mechanizmów uwierzytelniania działa z gRPC:

  • Microsoft Entra ID
  • Certyfikat klienta
  • IdentitySerwera
  • JWT Token
  • OAuth 2.0
  • OpenID Connect
  • WS-Federation

Aby uzyskać więcej informacji na temat konfigurowania uwierzytelniania na serwerze, zobacz ASP.NET Core authentication (Uwierzytelnianie podstawowe ASP.NET).

Skonfigurowanie klienta gRPC do korzystania z uwierzytelniania będzie zależeć od używanego mechanizmu uwierzytelniania. Poprzednie przykłady tokenu elementu nośnego i certyfikatu klienta pokazują kilka sposobów konfigurowania klienta gRPC do wysyłania metadanych uwierzytelniania za pomocą wywołań gRPC:

  • Silnie typizowane klienci gRPC używają HttpClient wewnętrznie. Uwierzytelnianie można skonfigurować w systemie HttpClientHandlerlub przez dodanie wystąpień niestandardowych HttpMessageHandler do elementu HttpClient.
  • Każde wywołanie gRPC ma opcjonalny CallOptions argument. Nagłówki niestandardowe można wysyłać przy użyciu kolekcji nagłówków opcji.

Uwaga

Uwierzytelnianie systemu Windows (NTLM/Kerberos/Negotiate) nie może być używane z gRPC. Usługa gRPC wymaga protokołu HTTP/2, a protokół HTTP/2 nie obsługuje uwierzytelniania systemu Windows.

Autoryzowanie użytkowników do uzyskiwania dostępu do usług i metod usług

Domyślnie wszystkie metody w usłudze mogą być wywoływane przez nieuwierzytelnionych użytkowników. Aby wymagać uwierzytelniania, zastosuj [Authorize] atrybut do usługi:

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

Można użyć argumentów konstruktora i właściwości atrybutu [Authorize] , aby ograniczyć dostęp tylko do użytkowników pasujących do określonych zasad autoryzacji. Jeśli na przykład masz niestandardowe zasady autoryzacji o nazwie MyAuthorizationPolicy, upewnij się, że tylko użytkownicy pasujący do tych zasad mogą uzyskiwać dostęp do usługi przy użyciu następującego kodu:

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

Poszczególne metody usług mogą również mieć [Authorize] zastosowany atrybut. Jeśli bieżący użytkownik nie jest zgodny z zasadami zastosowanymi zarówno do metody, jak i klasy, zostanie zwrócony błąd do obiektu wywołującego:

[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) ..
    }
}

Dodatkowe zasoby