跳到主要内容

快速体验

阅读前建议先了解 PG 模式概述,明确 PostgreSQL、身份认证、云存储和权限模型的关系。

本文以一个最小待办应用为例,演示 CloudBase PostgreSQL 数据库下从建表 → 启用 RLS → 授权 → 写策略 → SDK 调用的完整流程,帮你在 5~10 分钟内跑通一个安全的多用户数据访问链路。

前置准备

Key 的获取

Publishable Key / API Key 可在控制台创建(或通过 YUNAPI 的 CreateApiKey 创建)。

第一步:建表

创建一张 todos 表。可以使用 SQL 编辑器(控制台 → 数据库 → SQL 编辑器)直接执行:

CREATE TABLE public.todos (
id bigserial PRIMARY KEY,
title varchar(255) NOT NULL,
is_completed boolean NOT NULL DEFAULT false,
-- ⭐ owner_id 自动从 JWT 读取当前用户 ID
owner_id varchar(64) NOT NULL
DEFAULT (current_setting('request.jwt.claims', true)::json->>'sub'),
created_at timestamptz NOT NULL DEFAULT now()
);

CREATE INDEX idx_todos_owner_id ON public.todos(owner_id);

关键:owner_id 的默认值绑定 JWT sub,前端无需传入 owner_id,从源头杜绝身份伪造。

第二步:启用 RLS

ALTER TABLE public.todos ENABLE ROW LEVEL SECURITY;

启用 RLS 后,若未配置任何 Policy,所有非 service_role 用户都无法访问任何数据(默认拒绝)。继续下一步。

第三步:授予表级权限(GRANT)

表级 GRANT 控制每种角色能对表执行哪些类型的操作

-- 匿名用户:不给任何权限(未登录不能操作个人待办)

-- 登录用户:可读可写自己的待办
GRANT SELECT, INSERT, UPDATE, DELETE ON public.todos TO authenticated;
GRANT USAGE, SELECT ON SEQUENCE public.todos_id_seq TO authenticated;

-- 管理员:全部权限
GRANT ALL ON public.todos TO service_role;
GRANT USAGE, SELECT ON SEQUENCE public.todos_id_seq TO service_role;

第四步:创建 RLS 策略(Policy)

行级 Policy 控制每种角色能操作哪些具体的行

-- SELECT:登录用户仅能读自己的待办
CREATE POLICY todos_select_own ON public.todos
FOR SELECT TO authenticated
USING (owner_id = (current_setting('request.jwt.claims', true)::json->>'sub'));

-- INSERT:登录用户仅能写入 owner_id = 自己 的记录
CREATE POLICY todos_insert_own ON public.todos
FOR INSERT TO authenticated
WITH CHECK (owner_id = (current_setting('request.jwt.claims', true)::json->>'sub'));

-- UPDATE:登录用户仅能更新自己的记录,且不能把 owner_id 改成别人
CREATE POLICY todos_update_own ON public.todos
FOR UPDATE TO authenticated
USING (owner_id = (current_setting('request.jwt.claims', true)::json->>'sub'))
WITH CHECK (owner_id = (current_setting('request.jwt.claims', true)::json->>'sub'));

-- DELETE:登录用户仅能删自己的记录
CREATE POLICY todos_delete_own ON public.todos
FOR DELETE TO authenticated
USING (owner_id = (current_setting('request.jwt.claims', true)::json->>'sub'));
通过云 API 自动化部署

上述 SQL 可通过云 API ExecutePGSql 执行(适合 CI/CD)。注意 部分 DDL 在 API 路径下需要用 DO $$ BEGIN EXECUTE '...'; END $$ 包装后重试,详见 架构与权限模型 - ExecutePGSql

第五步:通过 SDK 读写数据

安装

npm install @cloudbase/js-sdk

使用

import cloudbase from '@cloudbase/js-sdk';

// 也可以传入 accessKey: '<Publishable Key>',这样未显式登录时即以 anon 身份访问
const app = cloudbase.init({ env: '<云开发环境 ID>' });
const auth = app.auth;

// 1. 登录(匿名 / 账号密码 / 第三方均可,此处以匿名为例)
// 匿名登录后 JWT 中 role=anon 但带真实 sub,可作为 owner_id 归属字段使用
await auth.signInAnonymously();

// 2. 访问 PG 数据库
const db = app.rdb();

// 查询:RLS 自动过滤,只返回当前用户的待办
const { data, error } = await db
.from('todos')
.select('*')
.eq('is_completed', false);

// 新增:owner_id 由数据库默认值自动填充为 JWT 的 sub
await db.from('todos').insert({ title: '写一篇文档' });

// 更新:RLS 只允许改自己的
await db.from('todos').update({ is_completed: true }).eq('id', 1);

// 删除:RLS 只允许删自己的
await db.from('todos').delete().eq('id', 1);
SDK 用法说明

第六步:直接使用 REST API(可选)

若无 SDK 场景,可直接调 PostgREST RESTful API:

# 登录
curl -X POST "https://<envId>.api.tcloudbasegateway.com/auth/v1/signin/anonymously" \
-H "Content-Type: application/json" \
-d '{}'
# 响应:{ "access_token": "eyJhbG...", ... }

# 查询
curl "https://<envId>.api.tcloudbasegateway.com/v1/rdb/rest/todos?select=*&is_completed=eq.false" \
-H "Authorization: Bearer <access_token>"

# 新增(owner_id 省略,由 DEFAULT 自动填充)
curl -X POST "https://<envId>.api.tcloudbasegateway.com/v1/rdb/rest/todos" \
-H "Authorization: Bearer <access_token>" \
-H "Prefer: return=representation" \
-H "Content-Type: application/json" \
-d '{"title":"写一篇文档"}'

验证 RLS 是否生效

  • 未登录访问(不带 Authorization 或用 anon 身份调用)→ 无权限,返回空数组或 403
  • 登录后访问 → 仅返回自己的数据
  • 伪造 owner_id(前端恶意传入 "owner_id": "other-user")→ 被 WITH CHECK 拒绝
  • 尝试读其他用户的待办(例如 ?owner_id=eq.other-user)→ 即使带过滤条件,USING 策略仍然只返回自己的数据

常见陷阱

  1. 启用了 RLS 但没写 Policy → 所有非 service_role 请求都被拒绝
  2. 写了 Policy 但忘记 GRANT → 表级权限不通过
  3. UPDATE 只写了 USING,没写 WITH CHECK → 用户可以把 owner_id 改成别人,窃取数据
  4. 在前端代码中使用 API Key → 等于所有数据完全暴露(service_role BYPASSRLS)
  5. 使用 serial / bigserial 主键忘记授予 SEQUENCE 权限 → INSERT 报权限错误
  6. DDL 通过 ExecutePGSql 时部分语句直接报错 → 失败时用 DO $$ BEGIN EXECUTE '...'; END $$ 包装重试

下一步