設定伺服器對伺服器生產應用程式的驗證作業

本指南說明如何設定伺服器對伺服器生產應用程式的驗證和授權作業。「驗證」是指判定用戶端身分的程序。「授權」是指判定經驗證的用戶端對一組資源具有哪些權限的程序。這表示驗證可識別您的身分,而授權則決定您可執行哪些操作。請參閱驗證總覽以深入瞭解支援的驗證方法以及如何從中選擇。

Google 使用憑證識別您的應用程式以處理配額和帳單。系統也會使用您的憑證授予 GCP API、資源和功能的存取權。

為您的應用程式提供憑證

GCP 用戶端程式庫使用名為「應用程式預設憑證」(ADC) 的策略來尋找應用程式的憑證。當您的程式碼使用用戶端程式庫時,此策略會依下列順序檢查您的憑證:

  1. 首先,ADC 會檢查是否已設定 GOOGLE_APPLICATION_CREDENTIALS 環境變數。如果已設定變數,ADC 就會使用該變數指向的服務帳戶檔案。下一節將說明如何設定環境變數。

  2. 如果尚未設定環境變數,ADC 就會針對在那些服務上執行的應用程式,使用 Compute Engine、Kubernetes Engine、App Engine 和 Cloud Functions 提供的預設服務帳戶。

  3. 如果 ADC 無法使用上述任一憑證,就會發生錯誤。

以下程式碼範例會說明此策略。此範例並未明確指定應用程式憑證,但是只要已設定 GOOGLE_APPLICATION_CREDENTIALS 環境變數或是正在 Compute Engine、Kubernetes Engine、App Engine 或 Cloud Functions 上執行應用程式,ADC 即可用隱含方式找到憑證。

您必須安裝 Cloud Storage 用戶端程式庫,才能執行下列範例。

C#

public object AuthImplicit(string projectId)
{
    // If you don't specify credentials when constructing the client, the
    // client library will look for credentials in the environment.
    var credential = GoogleCredential.GetApplicationDefault();
    var storage = StorageClient.Create(credential);
    // Make an authenticated API request.
    var buckets = storage.ListBuckets(projectId);
    foreach (var bucket in buckets)
    {
        Console.WriteLine(bucket.Name);
    }
    return null;
}

Go

// implicit uses Application Default Credentials to authenticate.
func implicit() {
	ctx := context.Background()

	// For API packages whose import path is starting with "cloud.google.com/go",
	// such as cloud.google.com/go/storage in this case, if there are no credentials
	// provided, the client library will look for credentials in the environment.
	storageClient, err := storage.NewClient(ctx)
	if err != nil {
		log.Fatal(err)
	}

	it := storageClient.Buckets(ctx, "project-id")
	for {
		bucketAttrs, err := it.Next()
		if err == iterator.Done {
			break
		}
		if err != nil {
			log.Fatal(err)
		}
		fmt.Println(bucketAttrs.Name)
	}

	// For packages whose import path is starting with "google.golang.org/api",
	// such as google.golang.org/api/cloudkms/v1, use the
	// golang.org/x/oauth2/google package as shown below.
	oauthClient, err := google.DefaultClient(ctx, cloudkms.CloudPlatformScope)
	if err != nil {
		log.Fatal(err)
	}

	kmsService, err := cloudkms.New(oauthClient)
	if err != nil {
		log.Fatal(err)
	}

	_ = kmsService
}

Java

static void authImplicit() {
  // If you don't specify credentials when constructing the client, the client library will
  // look for credentials via the environment variable GOOGLE_APPLICATION_CREDENTIALS.
  Storage storage = StorageOptions.getDefaultInstance().getService();

  System.out.println("Buckets:");
  Page<Bucket> buckets = storage.list();
  for (Bucket bucket : buckets.iterateAll()) {
    System.out.println(bucket.toString());
  }
}

Node.js

// Imports the Google Cloud client library.
const {Storage} = require('@google-cloud/storage');

// Instantiates a client. If you don't specify credentials when constructing
// the client, the client library will look for credentials in the
// environment.
const storage = new Storage();

// Makes an authenticated API request.
storage
  .getBuckets()
  .then(results => {
    const buckets = results[0];

    console.log('Buckets:');
    buckets.forEach(bucket => {
      console.log(bucket.name);
    });
  })
  .catch(err => {
    console.error('ERROR:', err);
  });

PHP

namespace Google\Cloud\Samples\Auth;

// Imports the Cloud Storage client library.
use Google\Cloud\Storage\StorageClient;

function auth_cloud_implicit($projectId)
{
    $config = [
        'projectId' => $projectId,
    ];

    # If you don't specify credentials when constructing the client, the
    # client library will look for credentials in the environment.
    $storage = new StorageClient($config);

    # Make an authenticated API request (listing storage buckets)
    foreach ($storage->buckets() as $bucket) {
        printf('Bucket: %s' . PHP_EOL, $bucket->name());
    }
}

Python

def implicit():
    from google.cloud import storage

    # If you don't specify credentials when constructing the client, the
    # client library will look for credentials in the environment.
    storage_client = storage.Client()

    # Make an authenticated API request
    buckets = list(storage_client.list_buckets())
    print(buckets)

Ruby

# project_id = "Your Google Cloud project ID"

require "google/cloud/storage"

# If you don't specify credentials when constructing the client, the client
# library will look for credentials in the environment.
storage = Google::Cloud::Storage.new project: project_id

# Make an authenticated API request
storage.buckets.each do |bucket|
  puts bucket.name
end

手動取得及提供服務帳戶憑證

如果您在本機開發程式碼、在內部部署應用程式,或部署到其他公用雲端環境,則可手動建立及取得服務帳戶憑證。

建立服務帳戶

下列步驟會說明如何建立服務帳戶。但是,您應該限制權限的存取權,而非設定擁有者層級的權限,詳情請參閱下列限制存取權一節的說明。

GCP 主控台

  1. 在 GCP 主控台中,前往「Create service account key」(建立服務帳戶金鑰) 頁面。

    前往「Create Service Account Key」(建立服務帳戶金鑰) 頁面
  2. 從 [Service account] (服務帳戶) 清單中選取 [New service account] (新增服務帳戶)
  3. 在 [Service account name] (服務帳戶名稱) 欄位中輸入一個名稱。
  4. 從 [Role] (角色) 清單中,選取 [Project] (專案) > [Owner] (擁有者)

    附註:「Role」(角色) 欄位會授權服務帳戶存取資源。以後您可以使用 GCP 主控台查看及變更這個欄位。如果您要開發正式版應用程式,請指定比 [Project] (專案) > [Owner] (擁有者) 更精細的權限。詳情請參閱為服務帳戶授予角色一文。
  5. 點選 [建立]。一個包含您金鑰的 JSON 檔案會下載到電腦中。

指令列

您可以使用本機電腦上的 Cloud SDK,或在 Cloud Shell 內執行下列指令。

  1. 建立服務帳戶。將 [NAME] 換成服務帳戶的名稱。

    gcloud iam service-accounts create [NAME]
  2. 向服務帳戶授予權限。用您的專案 ID 取代 [PROJECT_ID]

    gcloud projects add-iam-policy-binding [PROJECT_ID] --member "serviceAccount:[NAME]@[PROJECT_ID].iam.gserviceaccount.com" --role "roles/owner"
    附註:「Role」(角色) 欄位會授權服務帳戶存取資源。您稍後可以使用 GCP 主控台查看及變更這個欄位。如果您要開發正式版應用程式,請指定比 [Project] (專案) > [Owner] (擁有者) 更精細的權限。詳情請參閱為服務帳戶授予角色一文。
  3. 產生金鑰檔案。用金鑰檔案的名稱取代 [FILE_NAME]

    gcloud iam service-accounts keys create [FILE_NAME].json --iam-account [NAME]@[PROJECT_ID].iam.gserviceaccount.com

提供服務帳戶憑證

建立服務帳戶之後,有兩種方法可讓您為應用程式提供憑證。第一種是明確地設定 GOOGLE_APPLICATION_CREDENTIALS 環境變數,第二種則是將路徑傳送至程式碼中的服務帳戶金鑰。

設定環境變數

設定環境變數 GOOGLE_APPLICATION_CREDENTIALS 來為應用程式程式碼提供驗證憑證。 將 [PATH] 改成包含您的服務帳戶金鑰的 JSON 檔案路徑,並將 [FILE_NAME] 改成檔案名稱。 此變數僅適用於您目前的殼層工作階段,所以如果您開啟新的工作階段,請再次設定變數。

Linux 或 macOS

export GOOGLE_APPLICATION_CREDENTIALS="[PATH]"

例如:

export GOOGLE_APPLICATION_CREDENTIALS="/home/user/Downloads/[FILE_NAME].json"

Windows

使用 PowerShell:

$env:GOOGLE_APPLICATION_CREDENTIALS="[PATH]"

例如:

$env:GOOGLE_APPLICATION_CREDENTIALS="C:\Users\username\Downloads\[FILE_NAME].json"

使用命令提示字元:

set GOOGLE_APPLICATION_CREDENTIALS=[PATH]

當您完成上述步驟之後,ADC 即可用隱含方式判斷您的憑證,詳情請參閱上述為應用程式提供憑證一節。我們建議您採用此方法,這麼做需要的程式碼較少。

將路徑傳送至程式碼中的服務帳戶金鑰

您也可以選擇明確指向程式碼中的服務帳戶檔案,如下列程式碼範例所示。

您必須安裝 Cloud Storage 用戶端程式庫,才能執行下列範例。

C#

// Some APIs, like Storage, accept a credential in their Create()
// method.
public object AuthExplicit(string projectId, string jsonPath)
{
    // Explicitly use service account credentials by specifying
    // the private key file.
    var credential = GoogleCredential.FromFile(jsonPath);
    var storage = StorageClient.Create(credential);
    // Make an authenticated API request.
    var buckets = storage.ListBuckets(projectId);
    foreach (var bucket in buckets)
    {
        Console.WriteLine(bucket.Name);
    }
    return null;
}
// Other APIs, like Language, accept a channel in their Create()
// method.
public object AuthExplicit(string projectId, string jsonPath)
{
    var credential = GoogleCredential.FromFile(jsonPath)
        .CreateScoped(LanguageServiceClient.DefaultScopes);
    var channel = new Grpc.Core.Channel(
        LanguageServiceClient.DefaultEndpoint.ToString(),
        credential.ToChannelCredentials());
    var client = LanguageServiceClient.Create(channel);
    AnalyzeSentiment(client);
    return 0;
}

Go

// explicit reads credentials from the specified path.
func explicit(jsonPath, projectID string) {
	ctx := context.Background()
	client, err := storage.NewClient(ctx, option.WithCredentialsFile(jsonPath))
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println("Buckets:")
	it := client.Buckets(ctx, projectID)
	for {
		battrs, err := it.Next()
		if err == iterator.Done {
			break
		}
		if err != nil {
			log.Fatal(err)
		}
		fmt.Println(battrs.Name)
	}
}

Java

static void authExplicit(String jsonPath) throws IOException {
  // You can specify a credential file by providing a path to GoogleCredentials.
  // Otherwise credentials are read from the GOOGLE_APPLICATION_CREDENTIALS environment variable.
  GoogleCredentials credentials = GoogleCredentials.fromStream(new FileInputStream(jsonPath))
        .createScoped(Lists.newArrayList("https://www.googleapis.com/auth/cloud-platform"));
  Storage storage = StorageOptions.newBuilder().setCredentials(credentials).build().getService();

  System.out.println("Buckets:");
  Page<Bucket> buckets = storage.list();
  for (Bucket bucket : buckets.iterateAll()) {
    System.out.println(bucket.toString());
  }
}

Node.js

// Imports the Google Cloud client library.
const {Storage} = require('@google-cloud/storage');

// Instantiates a client. Explicitly use service account credentials by
// specifying the private key file. All clients in google-cloud-node have this
// helper, see https://github.com/GoogleCloudPlatform/google-cloud-node/blob/master/docs/authentication.md
const storage = new Storage({
  projectId: 'project-id',
  keyFilename: '/path/to/keyfile.json',
});

// Makes an authenticated API request.
storage
  .getBuckets()
  .then(results => {
    const buckets = results[0];

    console.log('Buckets:');
    buckets.forEach(bucket => {
      console.log(bucket.name);
    });
  })
  .catch(err => {
    console.error('ERROR:', err);
  });

PHP

namespace Google\Cloud\Samples\Auth;

// Imports the Cloud Storage client library.
use Google\Cloud\Storage\StorageClient;

function auth_cloud_explicit($projectId, $serviceAccountPath)
{
    # Explicitly use service account credentials by specifying the private key
    # file.
    $config = [
        'keyFilePath' => $serviceAccountPath,
        'projectId' => $projectId,
    ];
    $storage = new StorageClient($config);

    # Make an authenticated API request (listing storage buckets)
    foreach ($storage->buckets() as $bucket) {
        printf('Bucket: %s' . PHP_EOL, $bucket->name());
    }
}

Python

def explicit():
    from google.cloud import storage

    # Explicitly use service account credentials by specifying the private key
    # file.
    storage_client = storage.Client.from_service_account_json(
        'service_account.json')

    # Make an authenticated API request
    buckets = list(storage_client.list_buckets())
    print(buckets)

Ruby

# project_id = "Your Google Cloud project ID"
# key_file   = "path/to/service-account.json"
require "google/cloud/storage"

# Explicitly use service account credentials by specifying the private key
# file.
storage = Google::Cloud::Storage.new project: project_id, keyfile: key_file

# Make an authenticated API request
storage.buckets.each do |bucket|
  puts bucket.name
end

在 Compute Engine、Kubernetes Engine、App Engine 彈性環境和 Cloud Functions 上取得憑證

如果您的應用程式是在 Compute EngineKubernetes EngineApp Engine 彈性環境Cloud Functions 上執行,您就不需要建立自己的服務帳戶。Compute Engine 內有系統自動為您建立的預設服務帳戶,此外您也可以視需要針對每個執行個體指派不同的服務帳戶。當您建立新的執行個體時,該執行個體會自動啟用為當做服務帳戶執行,而且具備一組預設的授權權限。詳情請參閱 Compute Engine 預設服務帳戶

如同上一節所述,當您設定服務帳戶之後,ADC 即可用隱含方式找到您的憑證,完全不需要變更程式碼。您也可以明確指定要特別使用 Compute Engine 憑證,如下列程式碼範例所示。

您必須安裝 Cloud Storage 用戶端程式庫,才能執行下列範例。

C#

// Some APIs, like Storage, accept a credential in their Create()
// method.
public object AuthExplicitComputeEngine(string projectId)
{
    // Explicitly request service account credentials from the compute
    // engine instance.
    GoogleCredential credential =
        GoogleCredential.FromComputeCredential();
    var storage = StorageClient.Create(credential);
    // Make an authenticated API request.
    var buckets = storage.ListBuckets(projectId);
    foreach (var bucket in buckets)
    {
        Console.WriteLine(bucket.Name);
    }
    return null;
}
// Other APIs, like Language, accept a channel in their Create()
// method.
public object AuthExplicitComputeEngine(string projectId)
{
    var credential = GoogleCredential.FromComputeCredential();
    var channel = new Grpc.Core.Channel(
        LanguageServiceClient.DefaultEndpoint.ToString(),
        credential.ToChannelCredentials());
    var client = LanguageServiceClient.Create(channel);
    AnalyzeSentiment(client);
    return 0;
}

Go

// explicitDefault finds the default credentials.
//
// It is very uncommon to need to explicitly get the default credentials in Go.
// Most of the time, client libraries can use Application Default Credentials
// without having to pass the credentials in directly. See implicit above.
func explicitDefault(projectID string) {
	ctx := context.Background()

	creds, err := google.FindDefaultCredentials(ctx, storage.ScopeReadOnly)
	if err != nil {
		log.Fatal(err)
	}
	client, err := storage.NewClient(ctx, option.WithCredentials(creds))
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println("Buckets:")
	it := client.Buckets(ctx, projectID)
	for {
		battrs, err := it.Next()
		if err == iterator.Done {
			break
		}
		if err != nil {
			log.Fatal(err)
		}
		fmt.Println(battrs.Name)
	}
}

Java

static void authCompute() {
  // Explicitly request service account credentials from the compute engine instance.
  GoogleCredentials credentials = ComputeEngineCredentials.create();
  Storage storage = StorageOptions.newBuilder().setCredentials(credentials).build().getService();

  System.out.println("Buckets:");
  Page<Bucket> buckets = storage.list();
  for (Bucket bucket : buckets.iterateAll()) {
    System.out.println(bucket.toString());
  }
}

Node.js

'use strict';

const {auth, Compute} = require('google-auth-library');

/**
 * This example directly instantiates a Compute client to acquire credentials.
 * Generally, you wouldn't directly create this class, rather call the
 * `auth.getClient()` method to automatically obtain credentials.
 */
async function main() {
  const client = new Compute({
    // Specifying the serviceAccountEmail is optional. It will use the default
    // service account if one is not defined.
    serviceAccountEmail: 'some-service-account@example.com',
  });
  const projectId = await auth.getProjectId();
  const url = `https://www.googleapis.com/dns/v1/projects/${projectId}`;
  const res = await client.request({url});
  console.log(res.data);
}

main().catch(console.error);

PHP

namespace Google\Cloud\Samples\Auth;

// Imports GCECredentials and the Cloud Storage client library.
use Google\Auth\Credentials\GCECredentials;
use Google\Cloud\Storage\StorageClient;

function auth_cloud_explicit_compute_engine($projectId)
{
    $gceCredentials = new GCECredentials();
    $config = [
        'projectId' => $projectId,
        'credentialsFetcher' => $gceCredentials,
    ];
    $storage = new StorageClient($config);

    # Make an authenticated API request (listing storage buckets)
    foreach ($storage->buckets() as $bucket) {
        printf('Bucket: %s' . PHP_EOL, $bucket->name());
    }
}

Python

def explicit_compute_engine(project):
    from google.auth import compute_engine
    from google.cloud import storage

    # Explicitly use Compute Engine credentials. These credentials are
    # available on Compute Engine, App Engine Flexible, and Container Engine.
    credentials = compute_engine.Credentials()

    # Create the client using the credentials and specifying a project ID.
    storage_client = storage.Client(credentials=credentials, project=project)

    # Make an authenticated API request
    buckets = list(storage_client.list_buckets())
    print(buckets)

Ruby

require "googleauth"
require "google/cloud/env"
require "google/cloud/storage"

# Explicitly use Compute Engine credentials and a project ID to create a new
# Cloud Storage client. These credentials are available on Compute Engine,
# App Engine Flexible, and Container Engine.
storage = Google::Cloud::Storage.new project: Google::Cloud.env.project_id,
                                     keyfile: Google::Auth::GCECredentials.new

# Make an authenticated API request
storage.buckets.each do |bucket|
  puts bucket.name
end

在 App Engine 標準環境中取得憑證

如果您的應用程式是在 App Engine 標準環境上執行,則可使用 App Engine App Identity API 取得憑證。

如同上一節所述,當您設定服務帳戶之後,ADC 即可用隱含方式找到您的憑證,完全不需要變更程式碼。您也可以明確指定要特別使用 App Engine 憑證,如下列程式碼範例所示。

您必須安裝 Cloud Storage 用戶端程式庫,才能執行下列範例。

PHP

namespace Google\Cloud\Samples\Auth;

// Imports AppIdentityCredentials and the Cloud Storage client library.
use Google\Auth\Credentials\AppIdentityCredentials;
use Google\Cloud\Storage\StorageClient;

function auth_cloud_explicit_app_engine($projectId)
{
    # Learn more about scopes at https://cloud.google.com/storage/docs/authentication#oauth-scopes
    $scope = 'https://www.googleapis.com/auth/devstorage.read_only';
    $gaeCredentials = new AppIdentityCredentials($scope);
    $config = [
        'projectId' => $projectId,
        'credentialsFetcher' => $gaeCredentials,
    ];
    $storage = new StorageClient($config);

    # Make an authenticated API request (listing storage buckets)
    foreach ($storage->buckets() as $bucket) {
        printf('Bucket: %s' . PHP_EOL, $bucket->name());
    }
}

Go

// explicitDefault finds the default credentials.
//
// It is very uncommon to need to explicitly get the default credentials in Go.
// Most of the time, client libraries can use Application Default Credentials
// without having to pass the credentials in directly. See implicit above.
func explicitDefault(projectID string) {
	ctx := context.Background()

	creds, err := google.FindDefaultCredentials(ctx, storage.ScopeReadOnly)
	if err != nil {
		log.Fatal(err)
	}
	client, err := storage.NewClient(ctx, option.WithCredentials(creds))
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println("Buckets:")
	it := client.Buckets(ctx, projectID)
	for {
		battrs, err := it.Next()
		if err == iterator.Done {
			break
		}
		if err != nil {
			log.Fatal(err)
		}
		fmt.Println(battrs.Name)
	}
}

Java

static void authAppEngineStandard() throws IOException {
  // Explicitly request service account credentials from the app engine standard instance.
  GoogleCredentials credentials = AppEngineCredentials.getApplicationDefault();
  Storage storage = StorageOptions.newBuilder().setCredentials(credentials).build().getService();

  System.out.println("Buckets:");
  Page<Bucket> buckets = storage.list();
  for (Bucket bucket : buckets.iterateAll()) {
    System.out.println(bucket.toString());
  }
}

Python

def explicit_app_engine(project):
    from google.auth import app_engine
    import googleapiclient.discovery

    # Explicitly use App Engine credentials. These credentials are
    # only available when running on App Engine Standard.
    credentials = app_engine.Credentials()

    # Explicitly pass the credentials to the client library.
    storage_client = googleapiclient.discovery.build(
        'storage', 'v1', credentials=credentials)

    # Make an authenticated API request
    buckets = storage_client.buckets().list(project=project).execute()
    print(buckets)

限制存取

僅將與適用的 GCP API、功能或資源互動時所需的授權權限授予您的應用程式。GCP 採用 Cloud Identity and Access Management (Cloud IAM) 來處理 API 存取權控管。建立服務帳戶時,您可以選擇 Cloud IAM 角色來限制存取權。驗證入門中的逐步導覽會指示您在建立服務帳戶時選擇 Owner 角色。不過,您隨時可以變更此值。詳情請參閱為服務帳戶授予角色

管理憑證的最佳做法

憑證會提供機密資料的存取權,而下列措施可協助保護這些資源的存取權。

  • 請勿在原始碼嵌入驗證的相關密鑰,例如 API 金鑰、OAuth 憑證和服務帳戶憑證。您可以使用指向應用程式原始碼外部憑證的環境變數,例如 Cloud Key Management Service

  • 請針對不同的情況使用不同的憑證,例如測試和實際工作環境。

  • 為防止第三方攔截憑證,請僅透過 HTTPS 轉移憑證。請勿以純文字格式或加入網址進行移轉。

  • 切莫將長效型憑證嵌入用戶端應用程式。舉例來說,請勿將服務帳戶憑證嵌入行動應用程式。第三方可藉由審查用戶端應用程式,輕易地找到及使用其中的憑證。

  • 撤銷不再需要的憑證

排解 API 錯誤

如要進一步瞭解如何排解失敗的 API 要求,請參閱 Cloud API 錯誤

後續步驟

本頁內容對您是否有任何幫助?請提供意見:

傳送您對下列選項的寶貴意見...

這個網頁