Проверка подлинности и авторизация в gRPC для ASP.NET Core

Note

Это не последняя версия этой статьи. В текущей версии см. версию .NET 10 этой статьи.

Warning

Эта версия ASP.NET Core больше не поддерживается. Дополнительные сведения см. в политике поддержки .NET и .NET Core. В текущей версии см. версию .NET 10 этой статьи.

Автор: Джеймс Ньютон-Кинг (James Newton-King)

Просмотреть или скачать образец кода (описание загрузки)

Проверка подлинности пользователей, вызывающих службу gRPC

gRPC можно использовать с проверкой подлинности ASP.NET Core, чтобы связать пользователя с каждым вызовом.

Ниже приведен пример Program.cs, в котором используется проверка подлинности gRPC и ASP.NET Core:

app.UseRouting();

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

app.MapGrpcService<GreeterService>();

Note

Порядок, в котором вы регистрируете ASP.NET Core middleware для аутентификации, имеет значение. Всегда вызывайте UseAuthentication и UseAuthorization после UseRouting и до UseEndpoints.

Механизм проверки подлинности, используемый приложением во время вызова, необходимо настроить. Конфигурация проверки подлинности добавляется в Program.cs и будет отличаться в зависимости от используемого в приложении механизма проверки подлинности.

После настройки проверки подлинности пользователь может быть доступен в методах службы gRPC через ServerCallContext.

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

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

Проверка подлинности маркера носителя

Клиент может предоставить маркер доступа для проверки подлинности. Сервер проверяет маркер и использует его для обнаружения пользователя.

На сервере аутентификация с использованием токена-документа настраивается посредством промежуточного слоя JWT Bearer.

В клиенте .NET gRPC токен можно отправить вместе с вызовами, используя коллекцию Metadata. Записи в коллекции Metadata отправляются с помощью вызова gRPC в виде заголовков 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;
}

Задайте маркер носителя с помощью CallCredentials

Настройка ChannelCredentials на канале — это альтернативный способ отправки токена службе с вызовами gRPC. Класс ChannelCredentials может включать CallCredentials, что предоставляет возможность автоматически задавать Metadata.

Преимущества использования CallCredentials:

  • Проверка подлинности централизованно настроена на канале. Токен не нужно вручную предоставлять в вызов gRPC.
  • Обратный CallCredentials.FromInterceptor вызов является асинхронным. При необходимости учетные данные вызова могут получить жетон учетных данных из внешней системы. Асинхронные методы внутри обратного вызова должны использовать CancellationToken на AuthInterceptorContext.

Note

CallCredentials применяется только в том случае, если канал защищен протоколом TLS. Отправка заголовков проверки подлинности через небезопасное подключение негативно влияет на безопасность и не должна выполняться в рабочих средах. Приложение может задать параметр CallCredentials в настройках канала, чтобы игнорировать это поведение и всегда использовать UnsafeUseInsecureChannelCallCredentials.

Учетные данные доступа в следующем примере настраивают канал связи для отправки токена при каждом вызове 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;
}

Создание токена носителя с помощью фабрики клиента gRPC

Фабрика клиента gRPC может создавать клиентов, отправляющих токен доступа с помощью AddCallCredentials. Этот метод доступен в Grpc.Net.ClientFactory версии 2.46.0 или более поздней.

Переданный делегат AddCallCredentials выполняется для каждого вызова 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;
    });

Внедрение зависимостей можно сочетать с AddCallCredentials. Перегрузка передает IServiceProvider в делегат, который можно использовать для получения службы, созданной с использованием DI, с помощью служб ограниченной и временной области действия.

Рассмотрим приложение, которое имеет:

  • Пользовательский ITokenProvider для получения токена носителя. ITokenProvider регистрируется в DI (внедрение зависимостей) с временной областью.
  • Фабрика клиента gRPC настроена для создания клиентов, которые вставляются в службы gRPC и контроллеры веб-API.
  • вызовы gRPC должны использовать ITokenProvider для получения токена носителя.
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}");
    }));

Предыдущий код:

  • Определяет ITokenProvider и AppTokenProvider. Эти типы обрабатывают получение токена аутентификации для вызовов gRPC.
  • Регистрирует тип AppTokenProvider для внедрения зависимостей с ограниченным сроком действия. AppTokenProvider кэширует токен, чтобы его нужно было вычислять только при первом вызове в пределах области.
  • Регистрирует тип GreeterClient в фабрике клиента.
  • Настраивает AddCallCredentials для этого клиента. Делегат выполняется при каждом вызове и добавляет в метаданные маркер, полученный от ITokenProvider.

Проверка подлинности сертификатов клиента

Клиент может также предоставить сертификат клиента для проверки подлинности. Проверка подлинности по сертификату происходит на уровне TLS, задолго до его попадания в ASP.NET Core. Когда запрос попадает в ASP.NET Core, пакет аутентификации клиентского сертификата позволяет обработать сертификат до ClaimsPrincipal.

Note

Сервер должен быть настроен для принятия сертификатов клиентов. Сведения о принятии сертификатов клиента в Kestrel, IIS и Azure см. в разделе Настройка проверки подлинности по сертификату в ASP.NET Core.

В клиенте .NET gRPC сертификат клиента добавляется в HttpClientHandler, который затем используется для создания клиента 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);
}

Другие механизмы проверки подлинности

Многие механизмы аутентификации в ASP.NET Core работают с gRPC.

  • Microsoft Entra ID
  • Сертификат клиента
  • IdentityServer
  • Токен JWT
  • OAuth 2.0
  • OpenID Connect
  • WS-Federation

Дополнительные сведения о настройке проверки подлинности на сервере см. в разделе Проверка подлинности в ASP.NET Core.

Настройка клиента gRPC для использования проверки подлинности будет зависеть от используемого механизма проверки подлинности. В предыдущих примерах маркера носителя и сертификата клиента показано несколько способов настройки клиента gRPC для отправки метаданных проверки подлинности с помощью вызовов gRPC:

  • Строго типизированные клиенты gRPC используют HttpClient внутренне. Проверку подлинности можно настроить с помощью HttpClientHandler или путем добавления пользовательских экземпляров HttpMessageHandler в HttpClient.
  • Каждый вызов gRPC имеет необязательный аргумент CallOptions. Пользовательские заголовки можно отправлять с помощью коллекции заголовков опции.

Note

Проверка подлинности Windows (NTLM/Kerberos/Negotiate) не может использоваться с gRPC. Для gRPC требуется HTTP/2, а HTTP/2 не поддерживает проверку подлинности Windows.

Авторизация пользователей для доступа к службам и методам служб

По умолчанию все методы в службе могут вызываться пользователями, не прошедшими проверку подлинности. Чтобы требовать проверку подлинности, примените к службе атрибут [Authorize]:

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

Можно использовать аргументы конструктора и свойства атрибута [Authorize], чтобы ограничить доступ только пользователями, соответствующими определенным политикам авторизации. Например, если имеется пользовательская политика авторизации с именем MyAuthorizationPolicy, используйте следующий код, чтобы доступ к службе могли получить только пользователи, соответствующие этой политике:

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

К отдельным методам службы также может применяться атрибут [Authorize]. Если текущий пользователь не соответствует политикам, применяемым как к методу, так и к классу, ошибка будет возвращена вызывающему объекту:

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

Дополнительные ресурсы

Просмотреть или скачать образец кода (описание загрузки)

Проверка подлинности пользователей, вызывающих службу gRPC

gRPC можно использовать с проверкой подлинности ASP.NET Core, чтобы связать пользователя с каждым вызовом.

Ниже приведен пример Startup.Configure, в котором используется проверка подлинности gRPC и ASP.NET Core:

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

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

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

Note

Порядок, в котором вы регистрируете ASP.NET Core middleware для аутентификации, имеет значение. Всегда вызывайте UseAuthentication и UseAuthorization после UseRouting и до UseEndpoints.

Механизм проверки подлинности, используемый приложением во время вызова, необходимо настроить. Конфигурация проверки подлинности добавляется в Startup.ConfigureServices и будет отличаться в зависимости от используемого в приложении механизма проверки подлинности.

После настройки проверки подлинности пользователь может быть доступен в методах службы gRPC через ServerCallContext.

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

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

Проверка подлинности маркера носителя

Клиент может предоставить маркер доступа для проверки подлинности. Сервер проверяет маркер и использует его для обнаружения пользователя.

На сервере аутентификация с использованием токена-документа настраивается посредством промежуточного слоя JWT Bearer.

В клиенте .NET gRPC токен можно отправить вместе с вызовами, используя коллекцию Metadata. Записи в коллекции Metadata отправляются с помощью вызова gRPC в виде заголовков 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;
}

Задайте маркер носителя с помощью CallCredentials

Настройка ChannelCredentials на канале — это альтернативный способ отправки токена службе с вызовами gRPC. Объект ChannelCredentials может включать CallCredentials, которые предоставляют способ автоматически задавать Metadata.

Преимущества использования CallCredentials:

  • Проверка подлинности централизованно настроена на канале. Токен не нужно вручную предоставлять для вызова gRPC.
  • Обратный CallCredentials.FromInterceptor вызов является асинхронным. При необходимости учетные данные вызова могут получить токен учетных данных из внешней системы. Асинхронные методы внутри обратного вызова должны использовать CancellationToken на AuthInterceptorContext.

Note

CallCredentials применяется только в том случае, если канал защищен протоколом TLS. Отправка заголовков проверки подлинности через небезопасное подключение негативно влияет на безопасность и не должна выполняться в рабочих средах. Приложение может задать параметр CallCredentials в настройках канала, чтобы игнорировать это поведение и всегда использовать UnsafeUseInsecureChannelCallCredentials.

Учетные данные в следующем примере настраивают канал так, чтобы токен отправлялся при каждом вызове 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;
}

Создание токена носителя с помощью фабрики клиента gRPC

Фабрика клиента gRPC может создавать клиентов, отправляющих Bearer-токен с помощью AddCallCredentials. Этот метод доступен в Grpc.Net.ClientFactory версии 2.46.0 или более поздней.

Переданный делегат AddCallCredentials выполняется для каждого вызова 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;
    });

Внедрение зависимостей можно сочетать с AddCallCredentials. Перегрузка передает IServiceProvider в делегат. Делегат может быть использован для получения службы, сконструированной через внедрение зависимостей, с использованием служб с ограниченной областью и временных служб.

Рассмотрим приложение, которое имеет:

  • Пользовательский ITokenProvider для получения токена носителя. ITokenProvider регистрируется в DI с ограниченным областью видимости сроком существования.
  • Фабрика клиента gRPC настроена для создания клиентов, которые вставляются в службы gRPC и контроллеры веб-API.
  • вызовы gRPC должны использовать ITokenProvider для получения токена носителя.
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}");
    }));

Предыдущий код:

  • Определяет ITokenProvider и AppTokenProvider. Эти типы обрабатывают разрешение аутентификационного токена для вызовов gRPC.
  • Регистрирует тип AppTokenProvider с DI в скоупированном времени существования. AppTokenProvider кэширует маркер, чтобы его нужно было вычислить только в первом вызове в заданной области.
  • Регистрирует тип GreeterClient в фабрике клиента.
  • Настраивает AddCallCredentials для этого клиента. Делегат исполняется при каждом вызове и добавляет в метаданные токен, полученный от ITokenProvider.

Проверка подлинности сертификатов клиента

Клиент может также предоставить сертификат клиента для проверки подлинности. Проверка подлинности по сертификату происходит на уровне TLS, задолго до его попадания в ASP.NET Core. Когда запрос попадает в ASP.NET Core, пакет проверки подлинности сертификата клиента позволяет обработать сертификат в ClaimsPrincipal.

Note

Сервер должен быть настроен для принятия сертификатов клиентов. Сведения о принятии сертификатов клиента в Kestrel, IIS и Azure см. в разделе Настройка проверки подлинности по сертификату в ASP.NET Core.

В клиенте .NET gRPC сертификат клиента добавляется в HttpClientHandler, который затем используется для создания клиента 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);
}

Другие механизмы проверки подлинности

Многие механизмы аутентификации в ASP.NET Core работают с gRPC:

  • Microsoft Entra ID
  • Сертификат клиента
  • IdentityServer
  • Токен JWT
  • OAuth 2.0
  • OpenID Connect
  • WS-Federation

Дополнительные сведения о настройке проверки подлинности на сервере см. в разделе Проверка подлинности в ASP.NET Core.

Настройка клиента gRPC для использования проверки подлинности будет зависеть от используемого механизма проверки подлинности. В предыдущих примерах маркера носителя и сертификата клиента показано несколько способов настройки клиента gRPC для отправки метаданных проверки подлинности с помощью вызовов gRPC:

  • Строго типизированные клиенты gRPC используют HttpClient внутренне. Проверку подлинности можно настроить с помощью HttpClientHandler или путем добавления пользовательских экземпляров HttpMessageHandler в HttpClient.
  • Каждый вызов gRPC имеет необязательный аргумент CallOptions. Пользовательские заголовки можно отправлять с помощью коллекции заголовков опции.

Note

Проверка подлинности Windows (NTLM/Kerberos/Negotiate) не может использоваться с gRPC. Для gRPC требуется HTTP/2, а HTTP/2 не поддерживает проверку подлинности Windows.

Авторизация пользователей для доступа к службам и методам служб

По умолчанию все методы в службе могут вызываться пользователями, не прошедшими проверку подлинности. Чтобы требовать проверку подлинности, примените к службе атрибут [Authorize]:

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

Можно использовать аргументы конструктора и свойства атрибута [Authorize], чтобы ограничить доступ только пользователями, соответствующими определенным политикам авторизации. Например, если имеется пользовательская политика авторизации с именем MyAuthorizationPolicy, используйте следующий код, чтобы доступ к службе могли получить только пользователи, соответствующие этой политике:

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

К отдельным методам службы также может применяться атрибут [Authorize]. Если текущий пользователь не соответствует политикам, применяемым как к методу, так и к классу, ошибка будет возвращена вызывающему объекту:

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

Методы расширения авторизации

Авторизацию также можно контролировать с помощью стандартных методов расширения авторизации ASP.NET Core, таких как AllowAnonymous и RequireAuthorization.

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

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

Дополнительные ресурсы