Authentifizierung und Autorisierung in gRPC für ASP.NET Core

Von James Newton-King

Beispielcode anzeigen oder herunterladen (Vorgehensweise zum Herunterladen)

Authentifizieren von Benutzern, die einen gRPC-Dienst aufrufen

gRPC kann mit der ASP.NET Core-Authentifizierung verwendet werden, um einen Benutzer mit jedem Aufruf zu verknüpfen.

Nachfolgend finden Sie ein Beispiel für Program.cs, das gRPC- und ASP.NET Core-Authentifizierung verwendet:

app.UseRouting();

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

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

Hinweis

Die Reihenfolge, in der Sie die ASP.NET Core-Middleware für die Authentifizierung registrieren, ist wichtig. Rufen Sie UseAuthentication und UseAuthorization immer nach UseRouting und vor UseEndpoints auf.

Der Authentifizierungsmechanismus, den Ihre App während eines Aufrufs verwendet, muss konfiguriert werden. Die Authentifizierungskonfiguration wird in Program.cs hinzugefügt und wird je nach dem von Ihrer App verwendeten Authentifizierungsmechanismus unterschiedlich sein. Beispiele für die Sicherung von ASP.NET Core-Apps finden Sie unter Authentifizierungsbeispiele.

Nachdem die Authentifizierung eingerichtet wurde, kann auf den Benutzer in einer Methode des gRPC-Diensts über ServerCallContext zugegriffen werden.

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

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

Bearertokenauthentifizierung

Der Client kann ein Zugriffstoken zur Authentifizierung bereitstellen. Der Server überprüft das Token und verwendet es zum Identifizieren des Benutzers.

Auf dem Server wird die Bearertokenauthentifizierung unter Verwendung der JWT Bearer-Middleware konfiguriert.

Im gRPC-Client von .NET kann das Token unter Verwendung der Metadata-Sammlung mit Aufrufen gesendet werden. Einträge in der Metadata-Sammlung werden mit einem gRPC-Aufruf als HTTP-Header gesendet:

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;
}

Das Konfigurieren von ChannelCredentials für einen Kanal ist eine alternative Möglichkeit, das Token mit gRPC-Aufrufen an den Dienst zu senden. ChannelCredentials kann CallCredentials enthalten, die eine Möglichkeit zum automatischen Festlegen von Metadata bieten. CallCredentials wird bei jedem gRPC-Aufruf ausgeführt, wodurch vermieden wird, dass an mehreren Stellen Code geschrieben werden muss, um das Token selbst zu übergeben.

Hinweis

CallCredentials werden nur angewendet, wenn der Kanal mit TLS gesichert ist. Das Senden von Authentifizierungsheadern über eine unsichere Verbindung hat Sicherheitsauswirkungen und sollte in Produktionsumgebungen vermieden werden. Eine App kann einen Kanal so konfigurieren, dass er dieses Verhalten ignoriert und immer CallCredentials verwendet, indem er UnsafeUseInsecureChannelCallCredentials auf einem Kanal festlegt.

Die Anmeldeinformationen im folgenden Beispiel konfigurieren den Kanal derart, dass das Token bei jedem gRPC-Aufruf gesendet wird:

private static GrpcChannel CreateAuthenticatedChannel(string address)
{
    var credentials = CallCredentials.FromInterceptor((context, metadata) =>
    {
        if (!string.IsNullOrEmpty(_token))
        {
            metadata.Add("Authorization", $"Bearer {_token}");
        }
        return Task.CompletedTask;
    });

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

Bearertoken mit gRPC-Clientfactory

Die gRPC-Clientfactory kann Clients erstellen, die mithilfe von AddCallCredentials ein Bearertoken senden. Diese Methode ist in Grpc.Net.ClientFactory Version 2.46.0 oder höher verfügbar.

Der an AddCallCredentials übergebene Delegate wird für jeden gRPC-Aufruf ausgeführt:

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;
    });

Abhängigkeitsinjektion (DI) kann mit AddCallCredentials vereint werden. Eine Überladung übergibt IServiceProvider an den Delegate, welcher dazu verwendet werden kann, einen von DI erstellten Dienst mithilfe von bereichsbezogenen und vorübergehenden Diensten abzurufen.

Betrachten Sie eine App mit folgenden Einstellungen:

  • Benutzerdefinierter ITokenProvider zum Abrufen eines Bearertokens. ITokenProvider ist in DI mit einer bereichsbezogenen Lebensdauer registriert.
  • Die gRPC-Clientfactory wird konfiguriert, um Clients zu erstellen, die in gRPC-Dienste und Web-API-Controller eingefügt werden.
  • gRPC-Aufrufe sollten ITokenProvider verwenden, um ein Bearertoken zu erhalten.
public interface ITokenProvider
{
    Task<string> GetTokenAsync();
}

public class AppTokenProvider : ITokenProvider
{
    private string _token;

    public async Task<string> GetTokenAsync()
    {
        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();
        metadata.Add("Authorization", $"Bearer {token}");
    }));

Der vorangehende Code:

  • Definiert ITokenProvider und AppTokenProvider. Diese Typen kümmern sich um das Auflösen des Authentifizierungstokens für gRPC-Aufrufe.
  • Registriert den AppTokenProvider Typ mit DI in einer bereichsbezogenen Lebensdauer. AppTokenProvider Speichert das Token zwischen, sodass nur der erste Aufruf im Bereich erforderlich ist, um es zu berechnen.
  • Registriert den Typ GreeterClient bei der Clientfactory.
  • Konfiguriert AddCallCredentials für diesen Client. Der Delegate wird jedes Mal ausgeführt, wenn ein Aufruf durchgeführt wird, und fügt das Token hinzu, das von ITokenProvider an die Metadaten zurückgegeben wird.

Clientzertifikatsauthentifizierung

Ein Client könnte alternativ ein Clientzertifikat zur Authentifizierung bereitstellen. Die Zertifikatsauthentifizierung erfolgt auf der TLS-Ebene, lange bevor sie überhaupt zum ASP.NET Core gelangt. Wenn die Anforderung in ASP.NET Core eingeht, können Sie mit dem Clientzertifikat-Authentifizierungspaket das Zertifikat in ein ClaimsPrincipal auflösen.

Hinweis

Der Server muss so konfiguriert werden, dass er Clientzertifikate akzeptiert. Informationen zum Akzeptieren von Clientzertifikaten in Kestrel, IIS und Azure finden Sie unter Konfigurieren der Zertifikatauthentifizierung in ASP.NET Core.

Im .NET gRPC-Client wird das Clientzertifikat zu HttpClientHandler hinzugefügt, das dann zur Erstellung des gRPC-Clients verwendet wird:

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);
}

Andere Authentifizierungsmechanismen

Viele von ASP.NET Core unterstützte Authentifizierungsmechanismen arbeiten mit gRPC:

  • Azure Active Directory
  • Clientzertifikat
  • IdentityServer
  • JWT-Token
  • OAuth 2.0
  • OpenID Connect
  • WS-Federation

Weitere Informationen zum Konfigurieren der Authentifizierung auf dem Server finden Sie unter ASP.NET Core-Authentifizierung.

Die Konfiguration des gRPC-Clients für die Verwendung der Authentifizierung hängt von dem von Ihnen verwendeten Authentifizierungsmechanismus ab. Die vorherigen Beispiele für Bearertoken und Clientzertifikate zeigen eine Reihe von Möglichkeiten, wie der gRPC-Client so konfiguriert werden kann, dass er Authentifizierungsmetadaten mit gRPC-Aufrufen sendet:

  • Stark typisierte gRPC-Clients verwenden intern HttpClient. Die Authentifizierung kann für HttpClientHandler oder durch Hinzufügen benutzerdefinierter HttpMessageHandler-Instanzen zu HttpClient konfiguriert werden.
  • Jeder gRPC-Aufruf hat ein optionales CallOptions-Argument. Benutzerdefinierte Header können über die Headersammlung der Option gesendet werden.

Hinweis

Die Windows-Authentifizierung (NTLM/Kerberos/Negotiate) kann nicht mit gRPC verwendet werden. gRPC erfordert HTTP/2, und HTTP/2 unterstützt keine Windows-Authentifizierung.

Autorisieren von Benutzern zum Zugriff auf Dienste und Dienstmethoden

Standardmäßig können alle Methoden in einem Dienst von nicht authentifizierten Benutzern aufgerufen werden. Wenden Sie das Attribut [Authorize] auf den Dienst an, um eine Authentifizierung zu erfordern:

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

Sie können die Konstruktorargumente und -eigenschaften des [Authorize]-Attributs verwenden, um den Zugriff auf Benutzer zu beschränken, die bestimmten Autorisierungsrichtlinien entsprechen. Wenn Sie z. B. eine benutzerdefinierte Autorisierungsrichtlinie namens MyAuthorizationPolicy besitzen, stellen Sie sicher, dass nur Benutzer, die dieser Richtlinie entsprechen, mit dem folgenden Code auf den Dienst zugreifen können:

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

Auf einzelne Dienstmethoden kann auch das Attribut [Authorize] angewendet werden. Wenn der aktuelle Benutzer nicht mit den Richtlinien übereinstimmt, die sowohl auf die Methode als auch auf die Klasse angewendet werden, wird ein Fehler an den Aufrufer zurückgegeben:

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

Zusätzliche Ressourcen

Beispielcode anzeigen oder herunterladen (Vorgehensweise zum Herunterladen)

Authentifizieren von Benutzern, die einen gRPC-Dienst aufrufen

gRPC kann mit der ASP.NET Core-Authentifizierung verwendet werden, um einen Benutzer mit jedem Aufruf zu verknüpfen.

Nachfolgend finden Sie ein Beispiel für Startup.Configure, das gRPC- und ASP.NET Core-Authentifizierung verwendet:

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

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

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

Hinweis

Die Reihenfolge, in der Sie die ASP.NET Core-Middleware für die Authentifizierung registrieren, ist wichtig. Rufen Sie UseAuthentication und UseAuthorization immer nach UseRouting und vor UseEndpoints auf.

Der Authentifizierungsmechanismus, den Ihre App während eines Aufrufs verwendet, muss konfiguriert werden. Die Authentifizierungskonfiguration wird in Startup.ConfigureServices hinzugefügt und wird je nach dem von Ihrer App verwendeten Authentifizierungsmechanismus unterschiedlich sein. Beispiele für die Sicherung von ASP.NET Core-Apps finden Sie unter Authentifizierungsbeispiele.

Nachdem die Authentifizierung eingerichtet wurde, kann auf den Benutzer in einer Methode des gRPC-Diensts über ServerCallContext zugegriffen werden.

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

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

Bearertokenauthentifizierung

Der Client kann ein Zugriffstoken zur Authentifizierung bereitstellen. Der Server überprüft das Token und verwendet es zum Identifizieren des Benutzers.

Auf dem Server wird die Bearertokenauthentifizierung unter Verwendung der JWT Bearer-Middleware konfiguriert.

Im gRPC-Client von .NET kann das Token unter Verwendung der Metadata-Sammlung mit Aufrufen gesendet werden. Einträge in der Metadata-Sammlung werden mit einem gRPC-Aufruf als HTTP-Header gesendet:

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;
}

Das Konfigurieren von ChannelCredentials für einen Kanal ist eine alternative Möglichkeit, das Token mit gRPC-Aufrufen an den Dienst zu senden. ChannelCredentials kann CallCredentials enthalten, die eine Möglichkeit zum automatischen Festlegen von Metadata bieten.

CallCredentials wird bei jedem gRPC-Aufruf ausgeführt, wodurch vermieden wird, dass an mehreren Stellen Code geschrieben werden muss, um das Token selbst zu übergeben. Beachten Sie, dass CallCredentials nur angewendet werden, wenn der Kanal mit TLS gesichert ist. CallCredentials werden nicht auf unsichere Kanäle ohne TLS angewendet.

Die Anmeldeinformationen im folgenden Beispiel konfigurieren den Kanal derart, dass das Token bei jedem gRPC-Aufruf gesendet wird:

private static GrpcChannel CreateAuthenticatedChannel(string address)
{
    var credentials = CallCredentials.FromInterceptor((context, metadata) =>
    {
        if (!string.IsNullOrEmpty(_token))
        {
            metadata.Add("Authorization", $"Bearer {_token}");
        }
        return Task.CompletedTask;
    });

    // SslCredentials is used here because this channel is using TLS.
    // CallCredentials can't be used with ChannelCredentials.Insecure on non-TLS channels.
    var channel = GrpcChannel.ForAddress(address, new GrpcChannelOptions
    {
        Credentials = ChannelCredentials.Create(new SslCredentials(), credentials)
    });
    return channel;
}

Bearertoken mit gRPC-Clientfactory

Die gRPC-Clientfactory kann Clients erstellen, die mithilfe von AddCallCredentials ein Bearertoken senden. Diese Methode ist in Grpc.Net.ClientFactory Version 2.46.0 oder höher verfügbar.

Der an AddCallCredentials übergebene Delegate wird für jeden gRPC-Aufruf ausgeführt:

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;
    });

Abhängigkeitsinjektion (DI) kann mit AddCallCredentials vereint werden. Eine Überladung übergibt IServiceProvider an den Delegate, welcher dazu verwendet werden kann, einen von DI erstellten Dienst mithilfe von bereichsbezogenen und vorübergehenden Diensten abzurufen.

Betrachten Sie eine App mit folgenden Einstellungen:

  • Benutzerdefinierter ITokenProvider zum Abrufen eines Bearertokens. ITokenProvider ist in DI mit einer bereichsbezogenen Lebensdauer registriert.
  • Die gRPC-Clientfactory wird konfiguriert, um Clients zu erstellen, die in gRPC-Dienste und Web-API-Controller eingefügt werden.
  • gRPC-Aufrufe sollten ITokenProvider verwenden, um ein Bearertoken zu erhalten.
public interface ITokenProvider
{
    Task<string> GetTokenAsync();
}

public class AppTokenProvider : ITokenProvider
{
    private string _token;

    public async Task<string> GetTokenAsync()
    {
        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();
        metadata.Add("Authorization", $"Bearer {token}");
    }));

Der vorangehende Code:

  • Definiert ITokenProvider und AppTokenProvider. Diese Typen kümmern sich um das Auflösen des Authentifizierungstokens für gRPC-Aufrufe.
  • Registriert den AppTokenProvider Typ mit DI in einer bereichsbezogenen Lebensdauer. AppTokenProvider Speichert das Token zwischen, sodass nur der erste Aufruf im Bereich erforderlich ist, um es zu berechnen.
  • Registriert den Typ GreeterClient bei der Clientfactory.
  • Konfiguriert AddCallCredentials für diesen Client. Der Delegate wird jedes Mal ausgeführt, wenn ein Aufruf durchgeführt wird, und fügt das Token hinzu, das von ITokenProvider an die Metadaten zurückgegeben wird.

Clientzertifikatsauthentifizierung

Ein Client könnte alternativ ein Clientzertifikat zur Authentifizierung bereitstellen. Die Zertifikatsauthentifizierung erfolgt auf der TLS-Ebene, lange bevor sie überhaupt zum ASP.NET Core gelangt. Wenn die Anforderung in ASP.NET Core eingeht, können Sie mit dem Clientzertifikat-Authentifizierungspaket das Zertifikat in ein ClaimsPrincipal auflösen.

Hinweis

Der Server muss so konfiguriert werden, dass er Clientzertifikate akzeptiert. Informationen zum Akzeptieren von Clientzertifikaten in Kestrel, IIS und Azure finden Sie unter Konfigurieren der Zertifikatauthentifizierung in ASP.NET Core.

Im .NET gRPC-Client wird das Clientzertifikat zu HttpClientHandler hinzugefügt, das dann zur Erstellung des gRPC-Clients verwendet wird:

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);
}

Andere Authentifizierungsmechanismen

Viele von ASP.NET Core unterstützte Authentifizierungsmechanismen arbeiten mit gRPC:

  • Azure Active Directory
  • Clientzertifikat
  • IdentityServer
  • JWT-Token
  • OAuth 2.0
  • OpenID Connect
  • WS-Federation

Weitere Informationen zum Konfigurieren der Authentifizierung auf dem Server finden Sie unter ASP.NET Core-Authentifizierung.

Die Konfiguration des gRPC-Clients für die Verwendung der Authentifizierung hängt von dem von Ihnen verwendeten Authentifizierungsmechanismus ab. Die vorherigen Beispiele für Bearertoken und Clientzertifikate zeigen eine Reihe von Möglichkeiten, wie der gRPC-Client so konfiguriert werden kann, dass er Authentifizierungsmetadaten mit gRPC-Aufrufen sendet:

  • Stark typisierte gRPC-Clients verwenden intern HttpClient. Die Authentifizierung kann für HttpClientHandler oder durch Hinzufügen benutzerdefinierter HttpMessageHandler-Instanzen zu HttpClient konfiguriert werden.
  • Jeder gRPC-Aufruf hat ein optionales CallOptions-Argument. Benutzerdefinierte Header können über die Headersammlung der Option gesendet werden.

Hinweis

Die Windows-Authentifizierung (NTLM/Kerberos/Negotiate) kann nicht mit gRPC verwendet werden. gRPC erfordert HTTP/2, und HTTP/2 unterstützt keine Windows-Authentifizierung.

Autorisieren von Benutzern zum Zugriff auf Dienste und Dienstmethoden

Standardmäßig können alle Methoden in einem Dienst von nicht authentifizierten Benutzern aufgerufen werden. Wenden Sie das Attribut [Authorize] auf den Dienst an, um eine Authentifizierung zu erfordern:

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

Sie können die Konstruktorargumente und -eigenschaften des [Authorize]-Attributs verwenden, um den Zugriff auf Benutzer zu beschränken, die bestimmten Autorisierungsrichtlinien entsprechen. Wenn Sie z. B. eine benutzerdefinierte Autorisierungsrichtlinie namens MyAuthorizationPolicy besitzen, stellen Sie sicher, dass nur Benutzer, die dieser Richtlinie entsprechen, mit dem folgenden Code auf den Dienst zugreifen können:

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

Auf einzelne Dienstmethoden kann auch das Attribut [Authorize] angewendet werden. Wenn der aktuelle Benutzer nicht mit den Richtlinien übereinstimmt, die sowohl auf die Methode als auch auf die Klasse angewendet werden, wird ein Fehler an den Aufrufer zurückgegeben:

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

Zusätzliche Ressourcen