认证

支持的授权机制

gRP 集成 SSL/TLS 并对服务端授权所使用的 SSL/TLS 进行了改良,对客户端和服务端交换的所有数据进行了加密。对客户端来讲提供了可选的机制提供凭证来获得共同的授权。

gRPC 提供通用的机制(后续进行描述)来对请求和应答附加基于元数据的凭证。当通过 gRPC 访问 Google API 时,会为一定的授权流程提供额外的获取访问令牌的支持,这将通过以下代码例子进行展示。警告:Google OAuth2 凭证应该仅用于连接 Google 的服务。把 Google 对应的 OAuth2 令牌发往非 Google 的服务会导致令牌被窃取用作冒充客户端来访问 Google 的服务。

为了减少复杂性和将混乱最小化, gRPC 以一个统一的凭证对象来进行工作。凭证可以是以下两类:

  • 调用凭证, 被附加在调用上(或者 C++ 里的 客户端上下文)。凭证可以用组合频道凭证来进行组合。一个组合频道凭证可以将一个频道凭证和一个调用凭证关联创建一个新的频道凭证。结果在这个频道上的每次调用会发送组合的调用凭证来作为授权数据。例如,一各频道凭证可以由一个Ssl 凭证和一个访问令牌凭证生成。结果是在这个频道上的每次调用都会发送对应的访问令牌。调用凭证可以用 组合凭证来组装。组装后的 调用凭证应用到一个客户端上下文里,将触发发送这两个调用凭证的授权数据。

这是个最简单的认证场景:一个客户端仅仅想认证服务器并且加密所有数据。

对于高级的用例比如改变根 CA 或使用客户端证书,可以在发送给工厂方法的 SslCredentialsOptions 参数里的相应选项进行设置。

通过 Google 进行认证

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

这个应用使用的频道凭证对象就像 里运行的应用一样使用服务账号。在前面的案例里,服务账号的密钥从环境变量 GOOGLE_APPLICATION_CREDENTIALS 对应的文件里加载。这些密钥被用来生成承载令牌附加在在相应频道的每次 RPC 调用里。对于 GCE 里运行的应用,可以在虚拟机设置的时候为其配置一个默认的服务账号和相应的 OAuth2 范围。在运行时,这个凭证被用来与认证系统通讯来获取 OAuth2 访问令牌并且把令牌用作在相应的频道上的 RPC 调用。

扩展 gRPC 支持其他的认证机制

相应的凭证插件 API 允许开发者开发自己的凭证插件。

  • MetadataCredentialsPlugin 抽象类包含需要被开发者创建的子类实现的纯虚方法GetMetadata。
  • MetadataCredentialsFromPlugin 方法可以从 MetadataCredentialsPlugin 创建一个调用者凭证。这类有个简单的凭证插件例子,是通过在自定义头了设置一个认证票据。

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

    2. grpc::Status GetMetadata(

    3. grpc::string_ref service_url, grpc::string_ref method_name,
    4. <span class="hljs-keyword">const</span> grpc::AuthContext&amp; channel_auth_context,
    5. <span class="hljs-built_in">std</span>::<span class="hljs-built_in"><span class="hljs-stl_container"><span class="hljs-built_in">multimap</span></span></span><span class="hljs-stl_container">&lt;grpc::</span><span class="hljs-built_in"><span class="hljs-stl_container"><span class="hljs-built_in">string</span></span></span><span class="hljs-stl_container">, grpc::</span><span class="hljs-built_in"><span class="hljs-stl_container"><span class="hljs-built_in">string</span></span></span><span class="hljs-stl_container">&gt;</span>* metadata) override {
    6. private:
      grpc::string ticket_;
      };

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

更深层次的集成可以通过在将 gRPC 的凭证实现以插件的形式集成进核心层。gRPC 内部也允许用其他加密机制来替换 SSL/TLS 。

这些授权机制将会在所有 gRPC 支持的语言里提供。以下的一些节里展示了上文提到的认证和授权在每种语言里如何实现:很快将会推出更多语言的支持。

通过 SSL/TLS 进行服务端授权和加密(Ruby)

  1. # Base case - No encryption
  2. stub = Helloworld::Greeter::Stub.new('localhost:50051', :this_channel_is_insecure)
  3. ...
  4. # With server authentication SSL/TLS
  5. creds = GRPC::Core::Credentials.new(load_certs) # load_certs typically loads a CA roots file
  6. stub = Helloworld::Greeter::Stub.new('localhost:50051', creds)
  1. // Base case - No encryption/authentication
  2. var channel = new Channel("localhost:50051", ChannelCredentials.Insecure);
  3. var client = new Greeter.GreeterClient(channel);
  4. ...
  5. // With server authentication SSL/TLS
  6. var channelCredentials = new SslCredentials(File.ReadAllText("roots.pem")); // Load a custom roots file.
  7. var channel = new Channel("myservice.example.com", channelCredentials);
  8. var client = new Greeter.GreeterClient(channel);

通过 SSL/TLS 进行服务端授权和加密 (Python)

通过 Google 进行授权 (Ruby)

基本案例 - 无加密/授权

  1. stub = Helloworld::Greeter::Stub.new('localhost:50051', :this_channel_is_insecure)

用无限制凭证进行授权 (推荐途径)

  1. require 'googleauth' # from http://www.rubydoc.info/gems/googleauth/0.1.0
  2. ...
  3. ssl_creds = GRPC::Core::ChannelCredentials.new(load_certs) # load_certs typically loads a CA roots file
  4. authentication = Google::Auth.get_application_default()
  5. call_creds = GRPC::Core::CallCredentials.new(authentication.updater_proc)
  6. combined_creds = ssl_creds.compose(call_creds)
  7. stub = Helloworld::Greeter::Stub.new('greeter.googleapis.com', combined_creds)

用 OAuth2 令牌进行认证(传统途径)

  1. require 'googleauth' # from http://www.rubydoc.info/gems/googleauth/0.1.0
  2. ...
  3. ssl_creds = GRPC::Core::ChannelCredentials.new(load_certs) # load_certs typically loads a CA roots file
  4. call_creds = GRPC::Core::CallCredentials.new(authentication.updater_proc)
  5. combined_creds = ssl_creds.compose(call_creds)
  6. stub = Helloworld::Greeter::Stub.new('greeter.googleapis.com', combined_creds)

通过 Google 进行授权 (Node.js)

基本案例 - 无加密/授权

  1. var stub = new helloworld.Greeter('localhost:50051', grpc.credentials.createInsecure());

用无限制凭证进行授权 (推荐途径)

  1. // Authenticating with Google
  2. var GoogleAuth = require('google-auth-library'); // from https://www.npmjs.com/package/google-auth-library
  3. ...
  4. var ssl_creds = grpc.credentials.createSsl(root_certs);
  5. (new GoogleAuth()).getApplicationDefault(function(err, auth) {
  6. var call_creds = grpc.credentials.createFromGoogleCredential(auth);
  7. var combined_creds = grpc.credentials.combineChannelCredentials(ssl_creds, call_creds);
  8. var stub = new helloworld.Greeter('greeter.googleapis.com', combined_credentials);
  9. });

用 OAuth2 令牌进行认证(传统途径)

  1. var GoogleAuth = require('google-auth-library'); // from https://www.npmjs.com/package/google-auth-library
  2. ...
  3. var ssl_creds = grpc.Credentials.createSsl(root_certs); // load_certs typically loads a CA roots file
  4. var scope = 'https://www.googleapis.com/auth/grpc-testing';
  5. (new GoogleAuth()).getApplicationDefault(function(err, auth) {
  6. if (auth.createScopeRequired()) {
  7. auth = auth.createScoped(scope);
  8. }
  9. var call_creds = grpc.credentials.createFromGoogleCredential(auth);
  10. var combined_creds = grpc.credentials.combineChannelCredentials(ssl_creds, call_creds);
  11. var stub = new helloworld.Greeter('greeter.googleapis.com', combined_credentials);
  12. });

基本案例 - 无加密/授权

  1. var channel = new Channel("localhost:50051", ChannelCredentials.Insecure);
  2. var client = new Greeter.GreeterClient(channel);
  3. ...

用无限制凭证进行授权 (推荐途径)

用 OAuth2 令牌进行认证(传统途径)

  1. using Grpc.Auth; // from Grpc.Auth NuGet package
  2. ...
  3. string scope = "https://www.googleapis.com/auth/grpc-testing";
  4. var googleCredential = await GoogleCredential.GetApplicationDefaultAsync();
  5. if (googleCredential.IsCreateScopedRequired)
  6. {
  7. googleCredential = googleCredential.CreateScoped(new[] { scope });
  8. }
  9. var channel = new Channel("greeter.googleapis.com", googleCredential.ToChannelCredentials());
  10. var client = new Greeter.GreeterClient(channel);
  11. ...

授权一个 gRPC 调用

  1. var channel = new Channel("greeter.googleapis.com", new SslCredentials()); // Use publicly trusted roots.
  2. var client = new Greeter.GreeterClient(channel);
  3. ...
  4. var googleCredential = await GoogleCredential.GetApplicationDefaultAsync();
  5. ...

通过 Google 进行授权 (PHP)

基本案例 - 无加密/授权

  1. - client = new helloworld\GreeterClient('localhost:50051', [
  2. 'credentials' => Grpc\ChannelCredentials::createInsecure(),
  3. ]);
  4. ...

用无限制凭证进行授权 (推荐途径)

Authenticate using scopeless credentials (recommended approach)

  1. function updateAuthMetadataCallback(- context)
  2. {
  3. - auth_credentials = ApplicationDefaultCredentials::getCredentials();
  4. return - auth_credentials->updateMetadata(- metadata = [], - context->service_url);
  5. }
  6. - channel_credentials = Grpc\ChannelCredentials::createComposite(
  7. Grpc\ChannelCredentials::createSsl(file_get_contents('roots.pem')),
  8. Grpc\CallCredentials::createFromPlugin('updateAuthMetadataCallback')
  9. );
  10. - opts = [
  11. 'credentials' => - channel_credentials
  12. ];
  13. - client = new helloworld\GreeterClient('greeter.googleapis.com', - opts);
  14. `

用 OAuth2 令牌进行认证(传统途径)

  1. // the environment variable "GOOGLE_APPLICATION_CREDENTIALS" needs to be set
  2. - scope = "https://www.googleapis.com/auth/grpc-testing";
  3. - auth = Google\Auth\ApplicationDefaultCredentials::getCredentials(- scope);
  4. - opts = [
  5. 'credentials' => Grpc\Credentials::createSsl(file_get_contents('roots.pem'));
  6. 'update_metadata' => - auth->getUpdateMetadataFunc(),
  7. ];
  8. - client = new helloworld\GreeterClient('greeter.googleapis.com', - opts);

通过 Google 进行授权 (Python)

基本案例 - 无加密/授权

  1. channel = implementations.insecure_channel('localhost', 50051)
  2. stub = helloworld_pb2.beta_create_Greeter_stub(channel)
  3. ...

用 OAuth2 令牌进行认证(传统途径)

  1. transport_creds = implementations.ssl_channel_credentials(open('roots.pem').read(), None, None)
  2. def oauth2token_credentials(context, callback):
  3. try:
  4. credentials = oauth2client.client.GoogleCredentials.get_application_default()
  5. scoped_credentials = credentials.create_scoped([scope])
  6. except Exception as error:
  7. callback([], error)
  8. return
  9. callback([('authorization', 'Bearer %s' % scoped_credentials.get_access_token().access_token)], None)
  10. auth_creds = implementations.metadata_plugin_credentials(oauth2token_credentials)
  11. channel_creds = implementations.composite_channel_credentials(transport_creds, auth_creds)
  12. channel = implementations.secure_channel('localhost', 50051, channel_creds)

原文: