Firebase Admin SDK 是一组服务器库,让您可以从特权环境与 Firebase 进行交互,以执行以下操作:对 Firebase Data Connect 服务执行查询和变更,以进行批量数据管理以及使用提权和模拟的凭据执行其他操作。
Admin SDK 为您提供了一个 API,用于在读写模式和只读模式下调用操作。借助只读操作,您可以放心地实现无法修改数据库中数据的管理功能。
Admin SDK 设置
如需开始在服务器上将 与 Firebase Data Connect 搭配使用,您首先需要安装和设置适用于Admin SDK 的 Node.js。
在脚本中初始化 Admin SDK
如需初始化 SDK,请导入 Data Connect 扩展程序,并 声明您的项目服务 ID 和位置。
import { initializeApp } from 'firebase-admin/app';
import { getDataConnect } from 'firebase-admin/data-connect';
// If you'd like to use OAuth2 flows and other credentials to log in,
// visit https://firebase.google.com/docs/admin/setup#initialize-sdk
// for alternative ways to initialize the SDK.
const app = initializeApp();
const dataConnect = getDataConnect({
serviceId: 'serviceId',
location: 'us-west2'
});
设计要与 Admin SDK 搭配使用的查询和变更
考虑到以下因素,Admin SDK 非常适合运行 Data Connect 操作。
了解 SDK 和 @auth(level: NO_ACCESS) 操作指令
由于 Admin SDK 具有特权,因此它可以执行您的任何
查询和变更,无论使用 @auth 指令 设置的访问权限级别如何,包括 NO_ACCESS 级别。
如果您在客户端操作旁边组织管理查询和变更,以便在 .gql 源文件中导入到管理脚本中,Firebase 建议您将管理操作标记为没有任何授权访问权限级别,或者更明确地将其设置为 NO_ACCESS。无论哪种方式,这都可以防止此类操作从客户端或其他非特权上下文中执行。
将 SDK 与 Data Connect 模拟器搭配使用
在原型和测试环境中,对本地数据执行数据植入和其他操作可能很有用。Admin SDK 可以忽略本地流程的身份验证和授权,因此可以简化您的 工作流程。 (您还可以明确选择通过用户模拟来遵守操作的身份验证和授权配置。)
设置 DATA_CONNECT_EMULATOR_HOST 环境变量后,Firebase Admin SDK 会自动连接到 Data Connect
模拟器:
export DATA_CONNECT_EMULATOR_HOST="127.0.0.1:9399"
有关详情,请参阅:
- 本地开发中的数据植入指南
- Data Connect 模拟器文档。
运行管理员操作
Admin SDK 适用于对关键数据执行特权操作。
Admin SDK 提供三组 API:
- 生成的 Admin SDK,这些是根据您的
gql定义生成的类型安全 SDK,与生成客户端 SDK 的方式相同。 - 用于运行任意 GraphQL 操作的通用接口,您的
代码在该接口中实现查询和变更,并将其传递给读写
executeGraphql方法或只读executeGraphqlRead方法。 - 用于批量数据操作的专用接口,该接口不使用
通用
executeGraphql方法,而是公开用于 变更操作的专用方法:insert、insertMany、upsert和upsertMany。
使用生成的 SDK 管理数据
您可以根据 gql 定义生成 Admin SDK
,与生成客户端 SDK 的方式相同。
生成的 Admin SDK 包含与 gql 定义对应的接口和函数,您可以使用这些接口和函数对数据库执行操作。例如,假设您为歌曲数据库生成了一个 SDK,以及一个查询 getSongs:
import { initializeApp } from "firebase-admin/app";
import { getSongs } from "@dataconnect/admin-generated";
const adminApp = initializeApp();
const songs = await getSongs(
{ limit: 4 },
{ impersonate: { unauthenticated: true } }
);
或者,如需指定连接器配置,请执行以下操作:
import { initializeApp } from "firebase-admin/app";
import { getDataConnect } from "firebase-admin/data-connect";
import {
connectorConfig,
getSongs,
} from "@dataconnect/admin-generated";
const adminApp = initializeApp();
const adminDc = getDataConnect(connectorConfig);
const songs = await getSongs(
adminDc,
{ limit: 4 },
{ impersonate: { unauthenticated: true } }
);
模拟未经身份验证的用户
Admin SDK 旨在从受信任的环境运行,因此可以不受限制地访问您的数据库。
使用 Admin SDK 运行公共操作时,您应避免使用完整管理员权限运行该操作(遵循最小权限原则)。相反,您应以模拟用户的身份(请参阅下一部分)或以模拟未经身份验证的用户的身份运行该操作。
未经身份验证的用户只能运行标记为 PUBLIC 的操作。
在上面的示例中,getSongs 查询以未经身份验证的用户的身份执行。
模拟用户
您还可以通过在 impersonate 选项中传递部分或
全部 Firebase Authentication 令牌来代表特定用户执行操作;您
至少必须在子声明中指定用户的用户 ID。(这与您可以在 Data Connect GraphQL 操作中引用的
auth.uid 服务器值
相同。)
当您模拟用户时,只有在您提供的用户数据通过 GraphQL 定义中指定的身份验证检查后,操作才会成功。
如果您从可公开访问的端点调用生成的 SDK,则至关重要的是,该端点需要进行身份验证,并且您需要在模拟用户之前验证身份验证令牌的完整性。
使用可调用的 Cloud Functions 时,系统会 自动验证身份验证令牌,您可以按以下示例所示的方式使用它:
import { HttpsError, onCall } from "firebase-functions/https";
export const callableExample = onCall(async (req) => {
const authClaims = req.auth?.token;
if (!authClaims) {
throw new HttpsError("unauthenticated", "Unauthorized");
}
const favoriteSongs = await getMyFavoriteSongs(
undefined,
{ impersonate: { authClaims } }
);
// ...
});
否则,请使用 Admin SDK's verifyIdToken 方法来验证和解码
身份验证令牌。例如,假设您的端点实现为
纯 HTTP 函数,并且您已使用 authorization 标头将 Firebase Authentication 令牌
传递给您的端点,这是标准做法:
import { getAuth } from "firebase-admin/auth";
import { onRequest } from "firebase-functions/https";
const auth = getAuth();
export const httpExample = onRequest(async (req, res) => {
const token = req.header("authorization")?.replace(/^bearer\s+/i, "");
if (!token) {
res.sendStatus(401);
return;
}
let authClaims;
try {
authClaims = await auth.verifyIdToken(token);
} catch {
res.sendStatus(401);
return;
}
const favoriteSongs = await getMyFavoriteSongs(
undefined,
{ impersonate: { authClaims } }
);
// ...
});
只有在从安全且不可公开访问的环境执行真正管理任务(例如数据迁移)时,您才应指定并非源自可验证来源的用户 ID:
// Never do this if end users can initiate execution of the code!
const favoriteSongs = await getMyFavoriteSongs(
undefined,
{ impersonate: { authClaims } }
);
以不受限制的访问权限运行
如果您要执行需要管理员级权限的操作,请从调用中省略 impersonate 参数:
await upsertSong(adminDc, {
title: songTitle_one,
instrumentsUsed: [Instrument.VOCAL],
});
以这种方式调用的操作具有对数据库的完整访问权限。如果您有仅用于管理目的的查询或变更,则应使用 @auth(level: NO_ACCESS) 指令定义它们。这样做可确保只有管理员级调用方才能执行这些操作。
使用 executeGraphql 方法管理数据
如果您需要执行未定义 gql
变更或查询的一次性操作,可以使用 executeGraphql 方法或只读
executeGraphqlRead 方法。
模拟未经身份验证的用户
使用 Admin SDK 运行公共操作时,您应避免使用完整管理员权限运行该操作(遵循最小权限原则)。相反,您应以模拟用户的身份
(请参阅下一部分)或以模拟
未经身份验证的用户的身份运行该操作。未经身份验证的用户只能运行标记为 PUBLIC 的操作。
// Query to get posts, with authentication level PUBLIC
const queryGetPostsImpersonation = `
query getPosts @auth(level: PUBLIC) {
posts {
description
}
}`;
// Attempt to access data as an unauthenticated user
const optionsUnauthenticated: GraphqlOptions<undefined> = {
impersonate: {
unauthenticated: true
}
};
// executeGraphql with impersonated unauthenticated user scope
const gqlResponse = await dataConnect.executeGraphql<UserData, undefined>(queryGetPostsImpersonation, optionsUnauthenticated);
模拟用户
在某些用例中,您可能希望脚本代表特定用户,根据有限的凭据修改用户数据。这种方法遵循最小权限原则。
如需使用此接口,请从遵循 Authentication 令牌格式的自定义 JWT 身份验证令牌中收集信息。 另请参阅自定义令牌指南。
// Get the current user's data
const queryGetUserImpersonation = `
query getUser @auth(level: USER) {
user(key: {uid_expr: "auth.uid"}) {
id,
name
}
}`;
// Impersonate a user with the specified auth claims
const optionsAuthenticated: GraphqlOptions<undefined> = {
impersonate: {
authClaims: {
sub: 'QVBJcy5ndXJ1'
}
}
};
// executeGraphql with impersonated authenticated user scope
const gqlResponse = await dataConnect.executeGraphql<UserData, undefined>(queryGetUserImpersonation, optionsAuthenticated);
// gqlResponse -> { "data": { "user": { "id": "QVBJcy5ndXJ1", "name": "Fred" } } }
使用管理凭据
如果您要执行需要管理员级权限的操作,请从调用中省略 impersonate 参数:
// User can be publicly accessible, or restricted to admins
const query = "query getProfile(id: AuthID) { user(id: $id) { id name } }";
interface UserData {
user: {
id: string;
name: string;
};
}
export interface UserVariables {
id: string;
}
const options:GraphqlOptions<UserVariables> = { variables: { id: "QVBJcy5ndXJ1" } };
// executeGraphql
const gqlResponse = await dataConnect.executeGraphql<UserData, UserVariables>(query, options);
// executeGraphqlRead (similar to previous sample but only for read operations)
const gqlResponse = await dataConnect.executeGraphqlRead<UserData, UserVariables>(query, options);
// gqlResponse -> { "data": { "user": { "id": "QVBJcy5ndXJ1", "name": "Fred" } } }
以这种方式调用的操作具有对数据库的完整访问权限。如果您有仅用于管理目的的查询或变更,则应使用 @auth(level: NO_ACCESS) 指令定义它们。这样做可确保只有管理员级调用方才能执行这些操作。
执行批量数据操作
Firebase 建议您使用 Admin SDK 对生产数据库执行批量数据操作。
SDK 提供了以下方法来处理批量数据。每种方法都会根据提供的实参构建和执行 GraphQL 变更。
// Methods of the bulk operations API
// dc is a Data Connect admin instance from getDataConnect
const resp = await dc.insert("movie" /*table name*/, data[0]);
const resp = await dc.insertMany("movie" /*table name*/, data);
const resp = await dc.upsert("movie" /*table name*/, data[0]);
const resp = await dc.upsertMany("movie" /*table name*/, data);
批量操作的性能注意事项
对后端的每个请求都会产生一次 Cloud SQL 往返,因此,您批处理的越多,吞吐量就越高。
但是,批次大小越大,生成的 SQL 语句就越长。当达到 PostgreSQL SQL 语句长度限制时,您会遇到错误。
在实践中,请通过实验找到适合您的工作负载的批次大小。
接下来怎么做?
- 了解如何使用 向数据库植入数据 Admin SDK
- 查看 API 的 Admin SDK。
- 使用 Firebase CLI 和 Google Cloud 控制台执行其他项目 管理操作,例如管理架构和连接器 以及管理服务和数据库。