进行身份验证并连接到数据库

连接要求

与 MongoDB 兼容的 Firestore 客户端需要满足以下要求:

  • 驱动程序必须以 load balanced 模式连接。这样可以防止驱动程序尝试了解它们所连接的确切服务器拓扑。
  • 驱动程序必须在启用了 SSL 的情况下进行连接。
  • 驱动程序必须停用可重试写入。与 MongoDB 兼容的 Firestore 不支持可重试写入。您无需停用可重试读取,因为它们受支持。

检索连接字符串

数据库连接字符串取决于数据库的 UID、数据库的位置和身份验证机制。以下说明介绍了连接字符串的构成方式。

确切的连接字符串取决于身份验证机制,但基本连接字符串采用以下格式:

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

您可以通过以下方式之一获取基本连接字符串:

控制台
  1. 在 Google Cloud 控制台中,前往数据库页面。

    前往“数据库”

  2. 在数据库列表中,点击相关数据库的数据库 ID。
  3. 浏览器面板会显示基本连接字符串。复制并使用此连接字符串连接到数据库。
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 角色。如需详细了解如何授予角色,请参阅管理对项目、文件夹和组织的访问权限

您也可以通过自定义角色或其他预定义角色来获取所需的权限。

创建用户并连接到数据库

如需为与 MongoDB 兼容的 Firestore 数据库创建用户,请使用以下方法之一:

Google Cloud 控制台
  1. 在 Google Cloud 控制台中,前往数据库页面。

    前往“数据库”

  2. 从数据库列表中选择一个数据库。
  3. 在导航菜单中,点击 Auth
  4. 点击添加用户
  5. 输入用户名
  6. 为新用户选择一个角色。
  7. 点击添加

    新用户的密码将显示在确认对话框中。

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"'
    替换以下内容:
    • PROJECT_NAME:您的项目的名称。
    • PROJECT_NUMBER项目编号
    • DATABASE_ID:数据库 ID。
    • USERNAME:您之前创建的用户名。
    • CONDITION_TITLE:相应条件的标题。此条件会将访问权限仅限于此数据库
Java

本部分提供了一个代码示例,演示了如何使用 Java 管理客户端库创建用户凭证和配置 IAM 政策。该示例使用 Firestore Admin Client 库创建用户名和密码,并使用 Google Cloud Resource Manager 库配置 IAM。

对于 Maven build,您可以使用以下坐标:

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。
  • LOCATION:数据库的位置。
  • DATABASE_ID:数据库 ID。

与 Google Auth 库连接

以下代码示例会注册一个 OIDC 回调处理程序,该处理程序使用Google Cloud 标准 OAuth 库

借助此库,您可以使用多种不同类型的身份验证(应用默认凭证、工作负载身份联合)。

这需要将 Auth 库添加为依赖项

// 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:您的项目的名称。
  • LOCATION:数据库的位置。
  • DATABASE_ID:数据库 ID。

从 Google Cloud 计算环境连接

本部分介绍了如何从 Google Cloud计算环境(例如 Compute Engine 或 Cloud Run 服务或作业)连接到与 MongoDB 兼容的 Firestore。

从 Compute Engine 虚拟机连接

您可以使用 Compute Engine 服务账号对数据库进行身份验证并连接到数据库。为此,请为包含数据库的 Google Cloud 项目创建 IAM 政策。

准备工作

为虚拟机配置用户管理的服务账号:

请参阅配置凭证部分中的说明,完成 Compute Engine 服务账号的 IAM 政策配置。

从 Cloud Run 连接

您可以使用 Cloud Run 服务账号对数据库进行身份验证并连接到数据库。为此,请为包含数据库的 Google Cloud 项目创建 IAM 政策。

准备工作

请参阅配置凭证部分中的说明,完成 Cloud Run 服务账号的 IAM 政策配置。

配置凭据

如需为服务账号授予读写 Firestore 的 roles/datastore.user 角色,请运行以下命令:

gcloud projects add-iam-policy-binding PROJECT_NAME --member="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:您的项目的名称。
  • LOCATION:数据库的位置。
  • DATABASE_ID:数据库 ID。

如需详细了解如何检索 UID 和位置,请参阅检索连接字符串

使用临时访问令牌连接

您可以使用 Google Cloud 的临时访问令牌来运行 mongosh 等诊断工具。您可以使用 gcloud auth print-access-token 通过短期访问令牌进行身份验证。该令牌的有效期为 1 小时。

例如,使用以下命令通过 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
  • LOCATION:数据库位置
  • DATABASE_ID:数据库 ID