身份验证

gRPC 身份验证概述,包括内置身份验证机制以及如何插入您自己的身份验证系统。

身份验证

gRPC 身份验证概述,包括内置身份验证机制以及如何插入您自己的身份验证系统。

概述

gRPC 被设计为与各种身份验证机制一起工作,从而可以轻松安全地使用 gRPC 与其他系统通信。您可以使用我们支持的机制 - 使用或不使用 Google 基于令牌的身份验证的 SSL/TLS - 或者您可以通过扩展我们提供的代码来插入您自己的身份验证系统。

gRPC 还提供了一个简单的身份验证 API,允许您在创建通道或进行调用时以 Credentials 的形式提供所有必要的身份验证信息。

支持的身份验证机制

以下身份验证机制内置于 gRPC

  • SSL/TLS:gRPC 具有 SSL/TLS 集成,并提倡使用 SSL/TLS 来验证服务器,并加密客户端和服务器之间交换的所有数据。可选机制可用于客户端提供证书进行相互身份验证。
  • ALTS:如果应用程序运行在 Compute EngineGoogle Kubernetes Engine (GKE) 上,gRPC 支持 ALTS 作为传输安全机制。有关详细信息,请参阅以下特定于语言的页面之一:C++ 中的 ALTSGo 中的 ALTSJava 中的 ALTSPython 中的 ALTS
  • 使用 Google 的基于令牌的身份验证:gRPC 提供了一种通用机制(如下所述),用于将基于元数据的凭据附加到请求和响应。在通过 gRPC 访问 Google API 时,为某些身份验证流程提供了获取访问令牌(通常是 OAuth2 令牌)的额外支持:您可以在下面的代码示例中看到它的工作原理。一般来说,此机制必须与通道上的 SSL/TLS 一起使用 - Google 不允许没有 SSL/TLS 的连接,并且大多数 gRPC 语言实现不允许您在未加密的通道上发送凭据。

身份验证 API

gRPC 提供了一个简单的身份验证 API,它基于统一的 Credentials 对象概念,该对象可以在创建整个 gRPC 通道或单个调用时使用。

凭据类型

凭据可以有两种类型

  • 通道凭据,它附加到 Channel,例如 SSL 凭据。
  • 调用凭据,它附加到调用(或 C++ 中的 ClientContext)。

您还可以将它们组合在 CompositeChannelCredentials 中,从而允许您指定通道的 SSL 详细信息以及在通道上进行的每个调用的调用凭据。CompositeChannelCredentialsChannelCredentialsCallCredentials 相关联以创建新的 ChannelCredentials。结果将在通道上进行的每次调用时发送与组成的 CallCredentials 关联的身份验证数据。

例如,您可以从 SslCredentialsAccessTokenCredentials 创建 ChannelCredentials。当应用于 Channel 时,结果将为此通道上的每次调用发送相应的访问令牌。

也可以使用 CompositeCallCredentials 组成单独的 CallCredentials。当在调用中使用时,结果 CallCredentials 将触发发送与两个 CallCredentials 关联的身份验证数据。

使用客户端 SSL/TLS

现在让我们看看 Credentials 如何与我们支持的身份验证机制之一一起工作。这是最简单的身份验证场景,其中客户端只想验证服务器并加密所有数据。该示例是用 C++ 编写的,但 API 对于所有语言都类似:您可以在下面的“示例”部分中看到如何在更多语言中启用 SSL/TLS。

// Create a default SSL ChannelCredentials object.
auto channel_creds = grpc::SslCredentials(grpc::SslCredentialsOptions());
// Create a channel using the credentials created in the previous step.
auto channel = grpc::CreateChannel(server_name, channel_creds);
// Create a stub on the channel.
std::unique_ptr<Greeter::Stub> stub(Greeter::NewStub(channel));
// Make actual RPC calls on the stub.
grpc::Status s = stub->sayHello(&context, *request, response);

对于高级用例,例如修改根 CA 或使用客户端证书,可以在传递给工厂方法的 SslCredentialsOptions 参数中设置相应的选项。

使用基于 OAuth 令牌的身份验证

OAuth 2.0 协议是行业标准的授权协议。它使网站或应用程序能够使用 OAuth 令牌获得对用户帐户的有限访问权限。

gRPC 提供了一组简单的 API 来将 OAuth 2.0 集成到应用程序中,从而简化了身份验证。

在高层次上,使用基于 OAuth 令牌的身份验证包括 3 个步骤

  1. 在客户端获取或生成 OAuth 令牌。
    • 您可以按照以下说明生成特定于 Google 的令牌。
  2. 使用 OAuth 令牌创建凭据。
    • OAuth 令牌始终是每次调用凭据的一部分,您还可以将每次调用凭据附加到某些通道凭据。
    • 令牌将发送到服务器,通常作为 HTTP Authorization 头部的一部分。
  3. 服务器端验证令牌。
    • 在大多数实现中,验证是使用服务器端拦截器完成的。

有关如何在不同语言中使用 OAuth 令牌的详细信息,请参阅下面的示例。

使用基于 Google 令牌的身份验证

gRPC 应用程序可以使用一个简单的 API 来创建一个凭据,该凭据可用于在各种部署场景中与 Google 进行身份验证。 同样,我们的示例是 C++ 编写的,但您可以在我们的示例部分找到其他语言的示例。

auto creds = grpc::GoogleDefaultCredentials();
// Create a channel, stub and make RPC calls (same as in the previous example)
auto channel = grpc::CreateChannel(server_name, creds);
std::unique_ptr<Greeter::Stub> stub(Greeter::NewStub(channel));
grpc::Status s = stub->sayHello(&context, *request, response);

此通道凭据对象适用于使用服务帐户的应用程序以及在 Google Compute Engine (GCE) 中运行的应用程序。在前一种情况下,服务帐户的私钥从名为环境变量 GOOGLE_APPLICATION_CREDENTIALS 中指定的文件加载。 这些密钥用于生成承载令牌,这些令牌将附加到相应通道上的每个传出 RPC。

对于在 GCE 中运行的应用程序,可以在 VM 设置期间配置默认服务帐户和相应的 OAuth2 范围。在运行时,此凭据处理与身份验证系统的通信,以获取 OAuth2 访问令牌并将它们附加到相应通道上的每个传出 RPC。

扩展 gRPC 以支持其他身份验证机制

凭据插件 API 允许开发人员插入他们自己的凭据类型。 这包括:

  • MetadataCredentialsPlugin 抽象类,其中包含纯虚方法 GetMetadata,该方法需要由开发人员创建的子类实现。
  • MetadataCredentialsFromPlugin 函数,它从 MetadataCredentialsPlugin 创建一个 CallCredentials

这是一个简单的凭据插件示例,该插件在自定义标头中设置身份验证票证。

class MyCustomAuthenticator : public grpc::MetadataCredentialsPlugin {
 public:
  MyCustomAuthenticator(const grpc::string& ticket) : ticket_(ticket) {}

  grpc::Status GetMetadata(
      grpc::string_ref service_url, grpc::string_ref method_name,
      const grpc::AuthContext& channel_auth_context,
      std::multimap<grpc::string, grpc::string>* metadata) override {
    metadata->insert(std::make_pair("x-custom-auth-ticket", ticket_));
    return grpc::Status::OK;
  }

 private:
  grpc::string ticket_;
};

auto call_creds = grpc::MetadataCredentialsFromPlugin(
    std::unique_ptr<grpc::MetadataCredentialsPlugin>(
        new MyCustomAuthenticator("super-secret-ticket")));

通过在核心级别插入 gRPC 凭据实现,可以实现更深入的集成。 gRPC 内部也允许使用其他加密机制替换 SSL/TLS。

语言指南和示例

这些身份验证机制将在 gRPC 支持的所有语言中提供。 下表链接到演示各种语言的身份验证和授权的示例。

语言示例文档
C++不适用不适用
GoGo 示例Go 文档
JavaJava 示例 TLS (Java 示例 ATLS)Java 文档
PythonPython 示例Python 文档

基于 OAuth 令牌的身份验证的语言指南和示例

下表链接到演示各种语言中基于 OAuth 令牌的身份验证和授权的示例。

语言示例文档
C++不适用不适用
GoGo OAuth 示例Go OAuth 文档
JavaJava OAuth 示例Java OAuth 文档
PythonPython OAuth 示例Python OAuth 文档

其他示例

以下各节演示了上述身份验证和授权功能如何在上面未列出的其他语言中体现。

Ruby

基本情况 - 无加密或身份验证

stub = Helloworld::Greeter::Stub.new('localhost:50051', :this_channel_is_insecure)
...
使用服务器身份验证 SSL/TLS
creds = GRPC::Core::ChannelCredentials.new(load_certs)  # load_certs typically loads a CA roots file
stub = Helloworld::Greeter::Stub.new('myservice.example.com', creds)
使用 Google 进行身份验证
require 'googleauth'  # from http://www.rubydoc.info/gems/googleauth/0.1.0
...
ssl_creds = GRPC::Core::ChannelCredentials.new(load_certs)  # load_certs typically loads a CA roots file
authentication = Google::Auth.get_application_default()
call_creds = GRPC::Core::CallCredentials.new(authentication.updater_proc)
combined_creds = ssl_creds.compose(call_creds)
stub = Helloworld::Greeter::Stub.new('greeter.googleapis.com', combined_creds)

Node.js

基本情况 - 无加密/身份验证
var stub = new helloworld.Greeter('localhost:50051', grpc.credentials.createInsecure());
使用服务器身份验证 SSL/TLS
const root_cert = fs.readFileSync('path/to/root-cert');
const ssl_creds = grpc.credentials.createSsl(root_cert);
const stub = new helloworld.Greeter('myservice.example.com', ssl_creds);
使用 Google 进行身份验证
// Authenticating with Google
var GoogleAuth = require('google-auth-library'); // from https://npmjs.net.cn/package/google-auth-library
...
var ssl_creds = grpc.credentials.createSsl(root_certs);
(new GoogleAuth()).getApplicationDefault(function(err, auth) {
  var call_creds = grpc.credentials.createFromGoogleCredential(auth);
  var combined_creds = grpc.credentials.combineChannelCredentials(ssl_creds, call_creds);
  var stub = new helloworld.Greeter('greeter.googleapis.com', combined_credentials);
});
使用 OAuth2 令牌(旧方法)通过 Google 进行身份验证
var GoogleAuth = require('google-auth-library'); // from https://npmjs.net.cn/package/google-auth-library
...
var ssl_creds = grpc.Credentials.createSsl(root_certs); // load_certs typically loads a CA roots file
var scope = 'https://www.googleapis.com/auth/grpc-testing';
(new GoogleAuth()).getApplicationDefault(function(err, auth) {
  if (auth.createScopeRequired()) {
    auth = auth.createScoped(scope);
  }
  var call_creds = grpc.credentials.createFromGoogleCredential(auth);
  var combined_creds = grpc.credentials.combineChannelCredentials(ssl_creds, call_creds);
  var stub = new helloworld.Greeter('greeter.googleapis.com', combined_credentials);
});
使用服务器身份验证 SSL/TLS 和带有令牌的自定义标头
const rootCert = fs.readFileSync('path/to/root-cert');
const channelCreds = grpc.credentials.createSsl(rootCert);
const metaCallback = (_params, callback) => {
    const meta = new grpc.Metadata();
    meta.add('custom-auth-header', 'token');
    callback(null, meta);
}
const callCreds = grpc.credentials.createFromMetadataGenerator(metaCallback);
const combCreds = grpc.credentials.combineChannelCredentials(channelCreds, callCreds);
const stub = new helloworld.Greeter('myservice.example.com', combCreds);

PHP

基本情况 - 无加密/授权
$client = new helloworld\GreeterClient('localhost:50051', [
    'credentials' => Grpc\ChannelCredentials::createInsecure(),
]);
使用服务器身份验证 SSL/TLS
$client = new helloworld\GreeterClient('myservice.example.com', [
    'credentials' => Grpc\ChannelCredentials::createSsl(file_get_contents('roots.pem')),
]);
使用 Google 进行身份验证
function updateAuthMetadataCallback($context)
{
    $auth_credentials = ApplicationDefaultCredentials::getCredentials();
    return $auth_credentials->updateMetadata($metadata = [], $context->service_url);
}
$channel_credentials = Grpc\ChannelCredentials::createComposite(
    Grpc\ChannelCredentials::createSsl(file_get_contents('roots.pem')),
    Grpc\CallCredentials::createFromPlugin('updateAuthMetadataCallback')
);
$opts = [
  'credentials' => $channel_credentials
];
$client = new helloworld\GreeterClient('greeter.googleapis.com', $opts);
使用 OAuth2 令牌(旧方法)通过 Google 进行身份验证
// the environment variable "GOOGLE_APPLICATION_CREDENTIALS" needs to be set
$scope = "https://www.googleapis.com/auth/grpc-testing";
$auth = Google\Auth\ApplicationDefaultCredentials::getCredentials($scope);
$opts = [
  'credentials' => Grpc\Credentials::createSsl(file_get_contents('roots.pem'));
  'update_metadata' => $auth->getUpdateMetadataFunc(),
];
$client = new helloworld\GreeterClient('greeter.googleapis.com', $opts);

Dart

基本情况 - 无加密或身份验证
final channel = new ClientChannel('localhost',
      port: 50051,
      options: const ChannelOptions(
          credentials: const ChannelCredentials.insecure()));
final stub = new GreeterClient(channel);
使用服务器身份验证 SSL/TLS
// Load a custom roots file.
final trustedRoot = new File('roots.pem').readAsBytesSync();
final channelCredentials =
    new ChannelCredentials.secure(certificates: trustedRoot);
final channelOptions = new ChannelOptions(credentials: channelCredentials);
final channel = new ClientChannel('myservice.example.com',
    options: channelOptions);
final client = new GreeterClient(channel);
使用 Google 进行身份验证
// Uses publicly trusted roots by default.
final channel = new ClientChannel('greeter.googleapis.com');
final serviceAccountJson =
     new File('service-account.json').readAsStringSync();
final credentials = new JwtServiceAccountAuthenticator(serviceAccountJson);
final client =
    new GreeterClient(channel, options: credentials.toCallOptions);
验证单个 RPC 调用
// Uses publicly trusted roots by default.
final channel = new ClientChannel('greeter.googleapis.com');
final client = new GreeterClient(channel);
...
final serviceAccountJson =
     new File('service-account.json').readAsStringSync();
final credentials = new JwtServiceAccountAuthenticator(serviceAccountJson);
final response =
    await client.sayHello(request, options: credentials.toCallOptions);