认证
gRPC 认证概述,包括内置认证机制以及如何集成您自己的认证系统。
认证
概述
gRPC 设计用于支持多种认证机制,方便安全地使用 gRPC 与其他系统通信。您可以使用我们支持的机制——带或不带 Google 令牌认证的 SSL/TLS——或者通过扩展我们提供的代码来集成您自己的认证系统。
gRPC 还提供了一个简单的认证 API,允许您在创建通道或进行调用时将所有必要的认证信息作为 Credentials
提供。
支持的认证机制
gRPC 内置了以下认证机制:
- SSL/TLS:gRPC 集成了 SSL/TLS,并提倡使用 SSL/TLS 来认证服务器,并加密客户端与服务器之间交换的所有数据。还提供了可选机制,允许客户端提供证书进行双向认证。
- ALTS:如果应用运行在 ALTS 上,gRPC 支持 Compute Engine 或 Google Kubernetes Engine (GKE)。有关详细信息,请参阅以下特定语言的页面之一:C++ 中的 ALTS、Go 中的 ALTS、Java 中的 ALTS、Python 中的 ALTS。
- 基于 Google 令牌的认证:gRPC 提供了一种通用机制(如下所述),用于将基于元数据的凭据附加到请求和响应中。对于某些认证流程,还提供了通过 gRPC 访问 Google API 时获取访问令牌(通常是 OAuth2 令牌)的额外支持:您可以在下面的代码示例中看到其工作原理。一般来说,此机制必须与通道上的 SSL/TLS *一起*使用——Google 不允许不使用 SSL/TLS 的连接,并且大多数 gRPC 语言实现不允许您在未加密的通道上发送凭据。
警告
Google 凭据只能用于连接到 Google 服务。将 Google 颁发的 OAuth2 令牌发送给非 Google 服务可能会导致该令牌被盗,并被用于冒充客户端访问 Google 服务。认证 API
gRPC 提供了一个基于统一概念 Credentials 对象的简单认证 API,可用于创建整个 gRPC 通道或单个调用。
凭据类型
凭据有两种类型:
- 通道凭据,它们附加到
Channel
上,例如 SSL 凭据。 - 调用凭据,它们附加到调用上(或 C++ 中的
ClientContext
)。
您还可以将这些凭据组合到 CompositeChannelCredentials
中,以便指定例如通道的 SSL 详细信息以及在该通道上进行的每次调用的调用凭据。CompositeChannelCredentials
将 ChannelCredentials
和 CallCredentials
关联起来,创建一个新的 ChannelCredentials
。结果是,在该通道上进行的每次调用都会发送与组合的 CallCredentials
关联的认证数据。
例如,您可以从 SslCredentials
和 AccessTokenCredentials
创建一个 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
参数中设置相应的选项。
注意
非 POSIX 兼容系统(例如 Windows)需要在SslCredentialsOptions
中指定根证书,因为默认配置仅适用于 POSIX 文件系统。使用基于 OAuth 令牌的认证
OAuth 2.0 协议是业界标准的授权协议。它允许网站或应用程序使用 OAuth 令牌获取对用户账户的有限访问权限。
gRPC 提供了一组简单的 API,可将 OAuth 2.0 集成到应用程序中,从而简化认证流程。
从高层面看,使用基于 OAuth 令牌的认证包括 3 个步骤:
- 在客户端获取或生成 OAuth 令牌。
- 您可以按照以下说明生成 Google 特定的令牌。
- 使用 OAuth 令牌创建凭据。
- OAuth 令牌始终是每次调用凭据的一部分,您也可以将每次调用凭据附加到某些通道凭据。
- 令牌将被发送到服务器,通常作为 HTTP Authorization 头部的一部分。
- 服务器端验证令牌。
- 在大多数实现中,验证通过服务器端拦截器完成。
有关如何在不同语言中使用 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++ | N/A | N/A |
Go | Go 示例 | Go 文档 |
Java | Java TLS 示例 (Java ALTS 示例) | Java 文档 |
Python | Python 示例 | Python 文档 |
基于 OAuth 令牌认证的语言指南和示例
下表链接到在不同语言中演示基于 OAuth 令牌认证和授权的示例。
语言 | 示例 | 文档 |
---|---|---|
C++ | N/A | N/A |
Go | Go OAuth 示例 | Go OAuth 文档 |
Java | Java OAuth 示例 | Java OAuth 文档 |
Python | Python 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);