驗證並連線至資料庫

連線需求

Cloud Firestore 客戶必須提供下列資訊:

  • 駕駛人必須以 load balanced 模式連線。這樣一來,驅動程式就不必瞭解所連線的確切伺服器拓撲。
  • 驅動程式必須啟用 SSL 才能連線。
  • 驅動程式必須停用可重試的寫入作業。Cloud Firestore 不支援可重試的寫入作業。您不需要停用可重試的讀取作業,因為這項功能受到支援。

擷取連線字串

資料庫連線字串取決於資料庫的 UID、資料庫位置和驗證機制。下列操作說明描述如何形成連線字串。

確切的連線字串取決於驗證機制,但基本連線字串採用下列格式:

mongodb://UID.LOCATION.firestore.goog:443/DATABASE_ID?loadBalanced=true&tls=true&retryWrites=false

您可以透過下列其中一種方式取得基本連線字串:

Firebase 控制台
  1. 在 Firebase 控制台中,前往「Firestore Database」頁面。

    前往 Firestore 資料庫

  2. 按一下要驗證的資料庫。
  3. 在「Explorer」面板中,按一下「View more」圖示
  4. 選取「使用 MongoDB 工具連結」
  5. 複製連線字串。
gcloud

使用 gcloud firestore database describe 擷取 UID 和位置資訊:

gcloud firestore databases describe \
--database=DATABASE_ID \
--format='yaml(locationId, uid)'

DATABASE_ID 替換為資料庫 ID。

輸出內容會包含資料庫的位置和 UID。使用這項資訊建構基本連線字串。

使用基本連線字串和下列其中一種方法,驗證並連線至資料庫:

使用使用者名稱和密碼 (SCRAM) 連線

請按照下列步驟建立資料庫的使用者憑證,並連線至資料庫。

事前準備

如要取得建立使用者所需的權限,請要求系統管理員授予您資料庫的 userCredsAdmin (roles/datastore.userCredsAdmin) IAM 角色。如要進一步瞭解如何授予角色,請參閱「管理專案、資料夾和機構的存取權」。

您或許也能透過自訂角色或其他預先定義的角色,取得必要權限。

建立使用者並連線至資料庫

如要為 Cloud Firestore 資料庫建立使用者,請使用下列任一方法:

Firebase 控制台
  1. 在 Firebase 控制台中,前往「Firestore Database」頁面。

    前往 Firestore 資料庫

  2. 從資料庫清單中選取資料庫。
  3. 按一下導覽選單中的「Security」(安全性)
  4. 按一下「新增使用者」
  5. 輸入使用者名稱
  6. 為新使用者選取角色。
  7. 點選「Add user」
  8. 確認對話方塊會顯示新使用者的密碼。

gcloud CLI
  1. 如要使用 SCRAM 進行驗證,請先建立使用者憑證。使用 gcloud firestore user-creds 指令:
    gcloud firestore user-creds create USERNAME --database=DATABASE_ID
    取代下列項目:
    • USERNAME:要建立的使用者名稱。
    • DATABASE_ID:資料庫 ID。

    這項指令的輸出內容會包含使用者密碼。

    輸出結果會與下列內容相似:

    name: projects/PROJECT_NAME/databases/DATABASE_ID/userCreds/USERNAME
    resourceIdentity:
      principal: principal://firestore.googleapis.com/projects/PROJECT_NUMBER/name/databases/DATABASE_ID/userCreds/USERNAME
    securePassword: PASSWORD
  2. 根據預設,這個新使用者憑證沒有任何權限。如要取得資料庫的讀寫權限,請為這個特定資料庫新增 roles/datastore.user 角色:

    gcloud projects add-iam-policy-binding PROJECT_NAME \
    --member='principal://firestore.googleapis.com/projects/PROJECT_NUMBER/name/databases/DATABASE_ID/userCreds/USERNAME' \
    --role=roles/datastore.user \
    --condition='expression=resource.name == "projects/PROJECT_NAME/databases/DATABASE_ID",title="CONDITION_TITLE"'
    取代下列項目:
Java

本節提供程式碼範例,說明如何使用 Java 管理用戶端程式庫建立使用者憑證及設定 IAM 政策。這個範例會使用 Firestore Admin Client 程式庫建立使用者名稱和密碼,並使用 Google Cloud Resource Manager 程式庫設定 IAM。

如果是 Maven 建構作業,可以使用下列座標:

com.google.cloud:google-cloud-firestore-admin:3.33.1
com.google.cloud:google-cloud-resourcemanager:1.76.0

佈建使用者憑證和 IAM 政策:

import com.google.cloud.firestore.v1.FirestoreAdminClient;
import com.google.cloud.resourcemanager.v3.ProjectName;
import com.google.cloud.resourcemanager.v3.ProjectsClient;
import com.google.firestore.admin.v1.CreateUserCredsRequest;
import com.google.firestore.admin.v1.GetUserCredsRequest;
import com.google.firestore.admin.v1.UserCreds;
import com.google.iam.v1.Binding;
import com.google.iam.v1.GetIamPolicyRequest;
import com.google.iam.v1.GetPolicyOptions;
import com.google.iam.v1.Policy;
import com.google.iam.v1.SetIamPolicyRequest;
import com.google.protobuf.FieldMask;
import com.google.type.Expr;

public class FirestoreUserCredsExample {
  /**
   *   Provision user credentials and configure an IAM policy to allow SCRAM authentication into the
   *   specified Firestore with Mongo Compatibility database.
   */
  private static void provisionFirestoreUserCredsAndIAM(
      String projectId, String databaseId, String userName) throws Exception {
    UserCreds userCreds = createUserCreds(projectId, databaseId, userName);

    // Note the password returned in the UserCreds proto - it cannot be retrieved again
    // after the initial call to the createUserCreds API.
    System.out.printf(
        "Created credentials for username: %s:\nIAM principal: %s\nPassword: [%s]\n",
        userName, userCreds.getResourceIdentity().getPrincipal(), userCreds.getSecurePassword());

    // Provision an IAM binding for the principal associated with these user credentials.
    updateIamPolicyForUserCreds(projectId, databaseId, userName, userCreds);

    // Emit the password again.
    System.out.printf(
        "Successfully configured IAM policy for database: %s, username: %s\n",
        databaseId, userName);
    System.out.printf("Please make a note of the password: [%s]\n", userCreds.getSecurePassword());
  }

  /** Provision new user credentials using the FirestoreAdminClient. */
  private static UserCreds createUserCreds(String projectId, String databaseId, String userName)
      throws Exception {
    FirestoreAdminClient firestoreAdminClient = FirestoreAdminClient.create();
    return firestoreAdminClient.createUserCreds(
        CreateUserCredsRequest.newBuilder()
            .setParent(String.format("projects/%s/databases/%s", projectId, databaseId))
            .setUserCredsId(userName)
            .build());
  }

  /** Update the IAM policy using the Resource Manager ProjectsClient. */
  private static void updateIamPolicyForUserCreds(
      String projectId, String databaseId, String userName, UserCreds userCreds) throws Exception {
    try (ProjectsClient projectsClient = ProjectsClient.create()) {
      ProjectName projectName = ProjectName.of(projectId);

      // Get the current IAM policy.
      Policy currentPolicy =
          projectsClient.getIamPolicy(
              GetIamPolicyRequest.newBuilder()
                  .setResource(projectName.toString())
                  .setOptions(GetPolicyOptions.newBuilder().setRequestedPolicyVersion(3).build())
                  .build());

      String role = "roles/datastore.user";
      String title = String.format("Conditional IAM binding for %s", userName);
      String expression =
          String.format("resource.name == \"projects/%s/databases/%s\"", projectId, databaseId);

      // Construct an updated IAM policy with an additional binding for the user credentials.
      Policy.Builder policyBuilder = currentPolicy.toBuilder();
      Binding newBinding =
          Binding.newBuilder()
              .setRole(role)
              .setCondition(Expr.newBuilder().setTitle(title).setExpression(expression).build())
              .addMembers(userCreds.getResourceIdentity().getPrincipal())
              .build();
      policyBuilder.addBindings(newBinding);

      // Update the policy
      SetIamPolicyRequest request =
          SetIamPolicyRequest.newBuilder()
              .setResource(projectName.toString())
              .setPolicy(policyBuilder.build())
              .setUpdateMask(FieldMask.newBuilder().addPaths("bindings").addPaths("etag").build())
              .build();
      System.out.println(request);

      Policy updatedPolicy = projectsClient.setIamPolicy(request);
      System.out.println("Policy updated successfully: " + updatedPolicy);
    }
  }
}
Python

本節提供程式碼範例,說明如何使用 Python 管理用戶端程式庫建立使用者憑證,以及設定 IAM 政策。這個範例使用 Google Cloud Firestore API 用戶端程式庫建立使用者名稱和密碼,並使用 Google Cloud Iam API 用戶端程式庫Google Cloud Resource Manager API 用戶端程式庫設定 IAM。

您可以使用 pip 工具安裝必要的 Python 程式庫:

pip install google-cloud-iam
pip install google-cloud-firestore
pip install google-cloud-resource-manager

佈建使用者憑證和 IAM 政策:

from google.cloud import resourcemanager_v3
from google.cloud.firestore_admin_v1 import FirestoreAdminClient
from google.cloud.firestore_admin_v1 import types
from google.iam.v1 import iam_policy_pb2
from google.iam.v1 import policy_pb2
from google.type import expr_pb2

def create_user_creds(project_id: str, database_id: str, user_name: str):
  """Provision new user credentials using the FirestoreAdminClient."""
  client = FirestoreAdminClient()
  request = types.CreateUserCredsRequest(
      parent=f'projects/{project_id}/databases/{database_id}',
      user_creds_id=user_name,
  )
  response = client.create_user_creds(request)
  return response

def update_iam_policy_for_user_creds(
    project_id: str, database_id: str, user_name: str, user_creds
):
  """Update the IAM policy using the Resource Manager ProjectsClient."""
  client = resourcemanager_v3.ProjectsClient()
  request = iam_policy_pb2.GetIamPolicyRequest()
  request.resource = f'projects/{project_id}'
  request.options.requested_policy_version = 3

  # Get the current IAM policy
  current_policy = client.get_iam_policy(request)

  # Construct an updated IAM policy with an additional binding
  # for the user credentials.
  updated_policy = policy_pb2.Policy()
  binding = policy_pb2.Binding()
  iam_condition = expr_pb2.Expr()

  iam_condition.title = f'Conditional IAM binding for {user_name}'
  iam_condition.expression = (
      f'resource.name == "projects/{project_id}/databases/{database_id}"'
  )

  binding.role = 'roles/datastore.user'
  binding.condition.CopyFrom(iam_condition)
  binding.members.append(user_creds.resource_identity.principal)
  updated_policy.bindings.append(binding)

  # Update the policy
  updated_policy.MergeFrom(current_policy)
  set_policy_request = iam_policy_pb2.SetIamPolicyRequest()
  set_policy_request.resource = f'projects/{project_id}'
  set_policy_request.policy.CopyFrom(updated_policy)

  final_policy = client.set_iam_policy(set_policy_request)
  print(f'Policy updated successfully {final_policy}')

def provision_firestore_user_creds_and_iam(
    project_id: str, database_id: str, user_name: str
):
  """Provision user credentials and configure an IAM policy."""
  user_creds = create_user_creds(project_id, database_id, user_name)

  # Note the password returned in the UserCreds proto - it cannot be
  # retrieved again after the initial call to the create_user_creds API.
  print(f'Created credentials for username: {user_name}')
  print(f'IAM principal: {user_creds.resource_identity.principal}')
  print(f'Password: [{user_creds.secure_password}]')

  # Provision an IAM binding for the principal associated with
  # these user credentials.
  update_iam_policy_for_user_creds(
      project_id, database_id, user_name, user_creds
  )

  # Emit the password again
  print(
      f'Successfully configured IAM policy for database: {database_id},'
      f' username: {user_name}'
  )
  print(f'Please make a note of the password: [{user_creds.secure_password}]')

使用下列連線字串,透過 SCRAM 連線至資料庫:

mongodb://USERNAME:PASSWORD@UID.LOCATION.firestore.goog:443/DATABASE_ID?loadBalanced=true&authMechanism=SCRAM-SHA-256&tls=true&retryWrites=false

更改下列內容:

  • USERNAME:使用者名稱。
  • PASSWORD:您為這個使用者產生的密碼。
  • UID:資料庫的 UID。例如:f116f93a-519c-208a-9a72-3ef6c9a1f081
  • LOCATION:資料庫的位置。
  • DATABASE_ID:資料庫 ID。

連結 Google 驗證程式庫

下列程式碼範例會註冊 OIDC 回呼處理常式,並使用Google Cloud 標準 OAuth 程式庫

這個程式庫可讓您使用多種驗證類型 (應用程式預設憑證、Workload Identity Federation)。

這需要將驗證程式庫新增為依附元件

// Maven
<dependency>
  <groupId>com.google.auth</groupId>
  <artifactId>google-auth-library-oauth2-http</artifactId>
  <version>1.19.0</version>
</dependency>

// Gradle
implementation 'com.google.auth:google-auth-library-oauth2-http:1.19.0'

以下程式碼範例示範如何連線:

val db = MongoClients.create(
    clientSettings(
      "DATABASE_UID",
      "LOCATION"
    ).build()
  ).getDatabase("DATABASE_ID")

/**
 *   Creates a connection to a Firestore with MongoDB Compatibility database.
 *   @param databaseUid The uid of the database to connect to as a string. For example: f116f93a-519c-208a-9a72-3ef6c9a1f081
 *   @param locationId The location of the database to connect to, for example: nam5, us-central1, us-east4 etc...
 *   @param environment Determines whether to try and fetch an authentication credential from the
 *   Compute Engine VM metadata service or whether to call gcloud.
 */
private static MongoClientSettings.Builder clientSettings(
  String databaseUid: String
  String locationId:String
): MongoClientSettings.Builder {
  MongoCredential credential =
    MongoCredential.createOidcCredential(null)
      .withMechanismProperty(
        MongoCredential.OIDC_CALLBACK_KEY,
        new MongoCredential.OidcCallback() {
          @Override
          MongoCredential.OidcCallbackResult onRequest(
MongoCredential.OidcCallbackContext context) {
     // Customize this credential builder for additional credential types.
     GoogleCredentials credentials = GoogleCredentials.getApplicationDefault();
            return new MongoCredential.OidcCallbackResult(
         credentials.getAccessToken().getTokenValue(),
         Duration.between(Instant.now(),
credentials.getAccessToken().getExpirationTime().toInstant()));
          }
        },
      );
  return MongoClientSettings.builder()
    .hosts(listOf(ServerAddress(
        "$databaseUid.$locationId.firestore.goog", 443)))
    .credential(credential)
    .applyToClusterSettings(builder ->
         builder.mode(ClusterConnectionMode.LOAD_BALANCED))
    ).applyToSslSettings(ssl -> ssl.enabled(true)).retryWrites(false);
}

更改下列內容:

  • DATABASE_UID:資料庫的 UID。例如: f116f93a-519c-208a-9a72-3ef6c9a1f081
  • LOCATION:資料庫位置。
  • DATABASE_ID 資料庫 ID。

Google Cloud 計算環境連線

本節說明如何從Google Cloud運算環境 (例如 Compute EngineCloud Run 服務/工作) 連線至 Cloud Firestore

透過 Compute Engine VM 連線

您可以使用 Compute Engine 服務帳戶驗證及連線至資料庫。如要這麼做,請為包含資料庫的專案建立 IAM 政策。Google Cloud

事前準備

為 VM 設定使用者代管服務帳戶:

請參閱「設定憑證」一節中的操作說明,為 Compute Engine 服務帳戶完成 IAM 政策設定。

從「Cloud Run」連線

您可以使用 Cloud Run 服務帳戶驗證及連線至資料庫。如要這麼做,請為包含資料庫的專案建立 IAM 政策。Google Cloud

事前準備

請參閱「設定憑證」一節中的操作說明,為 Cloud Run 服務帳戶完成 IAM 政策設定。

設定憑證

如要授予服務帳戶 roles/datastore.user 角色,以便讀取及寫入 Cloud Firestore,請執行下列指令:

gcloud projects add-iam-policy-binding PROJECT_NAME --member="serviceAccount:SERVICE_ACCOUNT_EMAIL" --role=roles/datastore.user

更改下列內容:

  • PROJECT_NAME:專案名稱。
  • SERVICE_ACCOUNT_EMAIL:您建立的服務帳戶電子郵件地址。

建構連線字串

請使用下列格式建構連線字串:

mongodb://DATABASE_UID.LOCATION.firestore.goog:443/DATABASE_ID?loadBalanced=true&tls=true&retryWrites=false&authMechanism=MONGODB-OIDC&authMechanismProperties=ENVIRONMENT:gcp,TOKEN_RESOURCE:FIRESTORE

更改下列內容:

  • DATABASE_UID:資料庫的 UID。例如: f116f93a-519c-208a-9a72-3ef6c9a1f081
  • LOCATION:資料庫位置。
  • DATABASE_ID 資料庫 ID。

如要進一步瞭解如何擷取 UID 和位置,請參閱「擷取連線字串」。

使用臨時存取權杖連線

您可以使用暫時性的 Google Cloud 存取權杖執行診斷工具,例如 mongosh。您可以使用 gcloud auth print-access-token,透過短期存取權杖進行驗證。這個權杖的有效期限為一小時。

舉例來說,使用下列指令即可透過 mongosh 連線至資料庫:

mongosh --tls \
      --username access_token --password $(gcloud auth print-access-token) \
      'mongodb://UID.LOCATION.firestore.goog:443/DATABASE_ID?loadBalanced=true&authMechanism=PLAIN&authSource=$external&retryWrites=false'

更改下列內容:

  • DATABASE_UID:資料庫的 UID。例如:f116f93a-519c-208a-9a72-3ef6c9a1f081
  • LOCATION:資料庫位置
  • DATABASE_ID:資料庫 ID