身份验证和授权

GRPC 中的身份验证和授权 ASP.NET Core

James 牛顿-k

查看或下载示例代码 (如何下载)

对调用 gRPC 服务的用户进行身份验证

gRPC 可以与ASP.NET Core authentication一起使用,以将用户与每个调用相关联。

下面是使用 gRPC 和 ASP.NET Core 身份验证的 Startup.Configure 的示例:

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

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

备注

注册 ASP.NET Core 身份验证中间件的顺序。 始终在 UseRouting 之后和 UseEndpoints之前调用 UseAuthenticationUseAuthorization

需要配置应用在调用期间使用的身份验证机制。 身份验证配置是在 Startup.ConfigureServices 中添加的,会根据应用使用的身份验证机制而有所不同。 有关如何保护 ASP.NET Core 应用的示例,请参阅身份验证示例

设置身份验证后,可以通过 ServerCallContext在 gRPC 服务方法中访问该用户。

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

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

持有者令牌身份验证

客户端可以提供用于身份验证的访问令牌。 服务器验证令牌并使用它来标识用户。

在服务器上,持有者令牌身份验证使用 JWT 持有者中间件进行配置。

在 .NET gRPC 客户端中,可通过调用以标头的形式发送令牌:

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

在通道上配置 ChannelCredentials 是使用 gRPC 调用将令牌发送到服务的一种替代方法。 每次进行 gRPC 调用时,都将运行该凭据,这样就无需在多个位置编写代码即可自行传递令牌。

以下示例中的凭据将通道配置为通过每个 gRPC 调用发送令牌:

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

客户端证书身份验证

客户端还可以提供用于身份验证的客户端证书。 证书身份验证发生在 TLS 级别,在它被 ASP.NET Core 之前。 当请求输入 ASP.NET Core 时,客户端证书身份验证包可让你将证书解析为 ClaimsPrincipal

备注

需要将主机配置为接受客户端证书。 有关在 Kestrel、IIS 和 Azure 中接受客户端证书的信息,请参阅配置主机以要求提供证书

在 .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
    {
        HttpClient = new HttpClient(handler)
    });

    return new Ticketer.TicketerClient(channel);
}

其他身份验证机制

许多支持 ASP.NET Core 的身份验证机制都适用于 gRPC:

  • Azure Active Directory
  • 客户端证书
  • IdentityServer
  • JWT 令牌
  • OAuth 2.0
  • OpenID Connect
  • WS-Federation

有关在服务器上配置身份验证的详细信息,请参阅ASP.NET Core authentication

将 gRPC 客户端配置为使用身份验证将取决于所使用的身份验证机制。 以前的持有者令牌和客户端证书示例显示了几种方法,gRPC 客户端可以配置为通过 gRPC 调用发送身份验证元数据:

  • 强类型化 gRPC 客户端在内部使用 HttpClient 可以在HttpClientHandler上配置身份验证,或者通过将自定义HttpMessageHandler实例添加到 HttpClient
  • 每个 gRPC 调用都有一个可选 CallOptions 参数。 可以使用选项的标头集合来发送自定义标头。

备注

Windows 身份验证(NTLM/Kerberos/协商)不能与 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) ..
    }
}

其他资源