跳到主要内容

基础权限

CloudBase PostgreSQL 数据库通过 行级安全策略(Row Level Security,RLS) 实现基础数据权限控制。

RLS 允许你在数据库层面定义访问策略,精确控制每一行数据的读写权限,确保用户只能访问被授权的数据。

RLS 机制简介

PostgreSQL 的 RLS 机制通过在表上创建安全策略(Policy)来控制行级别的数据访问。其核心流程为:

  1. 启用 RLS:对目标表开启行级安全策略
  2. 创建策略:定义允许用户执行哪些操作(SELECT、INSERT、UPDATE、DELETE)以及可访问哪些行
  3. 自动过滤:数据库在执行查询时,自动根据策略过滤数据行,对应用层完全透明

在 CloudBase 中,通过 auth.uid() 获取当前请求用户的身份标识,你可以在策略中将其与表中自定义的用户标识字段进行比较,实现数据归属控制。

CloudBase 为用户分配了以下数据库角色:

  • anon:未登录用户拥有的角色
  • authenticated:登录用户拥有的角色

你可以在 RLS 策略中结合角色和 auth.uid() 来实现更灵活的权限控制。

配置方式

通过执行 SQL 语句来配置表的权限策略。以下是几种常见场景的 Policy 创建示例。

提示

示例中使用 user_id 作为数据归属字段名,你可以根据实际表结构替换为其他字段。

读取全部数据,修改本人数据

适用于用户评论、用户公开信息等场景——登录用户可查看全部数据,但只有数据拥有者可以修改自己的数据。

-- 启用 RLS
ALTER TABLE my_table ENABLE ROW LEVEL SECURITY;

-- 允许登录用户读取全部数据
CREATE POLICY select_all ON my_table
FOR SELECT
TO authenticated
USING (true);

-- 仅允许用户修改自己的数据
CREATE POLICY update_own ON my_table
FOR UPDATE
TO authenticated
USING (user_id = auth.uid());

-- 仅允许用户删除自己的数据
CREATE POLICY delete_own ON my_table
FOR DELETE
TO authenticated
USING (user_id = auth.uid());

-- 插入时自动绑定数据归属
CREATE POLICY insert_own ON my_table
FOR INSERT
TO authenticated
WITH CHECK (user_id = auth.uid());

读取和修改本人数据

适用于用户个人设置、用户订单管理等场景——用户只能查看和操作自己的数据。

-- 启用 RLS
ALTER TABLE my_table ENABLE ROW LEVEL SECURITY;

-- 仅允许用户读取自己的数据
CREATE POLICY select_own ON my_table
FOR SELECT
TO authenticated
USING (user_id = auth.uid());

-- 仅允许用户修改自己的数据
CREATE POLICY update_own ON my_table
FOR UPDATE
TO authenticated
USING (user_id = auth.uid());

-- 仅允许用户删除自己的数据
CREATE POLICY delete_own ON my_table
FOR DELETE
TO authenticated
USING (user_id = auth.uid());

-- 插入时自动绑定数据归属
CREATE POLICY insert_own ON my_table
FOR INSERT
TO authenticated
WITH CHECK (user_id = auth.uid());

所有人可读数据(含未登录用户)

适用于公告信息、帮助文档等场景——任何人(包括未登录用户)都可以查看数据,无需登录。

-- 启用 RLS
ALTER TABLE my_table ENABLE ROW LEVEL SECURITY;

-- 允许所有人(含未登录用户)读取数据
CREATE POLICY select_anon ON my_table
FOR SELECT
TO anon, authenticated
USING (true);

读取全部数据,不可修改数据

适用于商品信息、系统配置等场景——登录用户可查看全部数据,但不允许通过客户端修改。

-- 启用 RLS
ALTER TABLE my_table ENABLE ROW LEVEL SECURITY;

-- 允许登录用户读取全部数据
CREATE POLICY select_all ON my_table
FOR SELECT
TO authenticated
USING (true);

-- 不创建 INSERT / UPDATE / DELETE 策略
-- RLS 启用后,未被策略允许的操作默认被拒绝

基于状态的条件访问

适用于文章发布、商品上下架等场景——登录用户可查看所有已发布的数据,作者还可以查看和修改自己的草稿。

-- 启用 RLS
ALTER TABLE my_table ENABLE ROW LEVEL SECURITY;

-- 登录用户可查看已发布数据,或自己的草稿
CREATE POLICY select_published_or_own ON my_table
FOR SELECT
TO authenticated
USING (status = 'published' OR user_id = auth.uid());

-- 仅允许用户修改自己的数据
CREATE POLICY update_own ON my_table
FOR UPDATE
TO authenticated
USING (user_id = auth.uid());

-- 仅允许用户删除自己的数据
CREATE POLICY delete_own ON my_table
FOR DELETE
TO authenticated
USING (user_id = auth.uid());

-- 插入时自动绑定数据归属
CREATE POLICY insert_own ON my_table
FOR INSERT
TO authenticated
WITH CHECK (user_id = auth.uid());

团队共享数据

适用于多租户、团队协作等场景——同一团队的成员可以互相查看和操作数据。

-- 启用 RLS
ALTER TABLE my_table ENABLE ROW LEVEL SECURITY;

-- 允许用户查看同一团队的数据
CREATE POLICY select_team ON my_table
FOR SELECT
TO authenticated
USING (team_id IN (SELECT team_id FROM team_members WHERE user_id = auth.uid()));

-- 允许用户修改同一团队的数据
CREATE POLICY update_team ON my_table
FOR UPDATE
TO authenticated
USING (team_id IN (SELECT team_id FROM team_members WHERE user_id = auth.uid()));

-- 允许用户在所属团队中插入数据
CREATE POLICY insert_team ON my_table
FOR INSERT
TO authenticated
WITH CHECK (team_id IN (SELECT team_id FROM team_members WHERE user_id = auth.uid()));

-- 允许用户删除同一团队的数据
CREATE POLICY delete_team ON my_table
FOR DELETE
TO authenticated
USING (team_id IN (SELECT team_id FROM team_members WHERE user_id = auth.uid()));

仅允许插入,不可修改删除

适用于用户反馈、操作日志等场景——登录用户可以提交数据,但提交后不可修改或删除。

-- 启用 RLS
ALTER TABLE my_table ENABLE ROW LEVEL SECURITY;

-- 允许登录用户查看自己提交的数据
CREATE POLICY select_own ON my_table
FOR SELECT
TO authenticated
USING (user_id = auth.uid());

-- 允许登录用户插入数据
CREATE POLICY insert_own ON my_table
FOR INSERT
TO authenticated
WITH CHECK (user_id = auth.uid());

-- 不创建 UPDATE / DELETE 策略
-- 数据提交后不可修改或删除

无权限

适用于后台流水数据、内部审计日志等场景——客户端用户不可直接访问,仅允许通过云函数等服务端逻辑操作。

-- 启用 RLS
ALTER TABLE my_table ENABLE ROW LEVEL SECURITY;

-- 不创建任何策略
-- RLS 启用后,所有客户端操作默认被拒绝
提示

对于"无权限"的表,你仍然可以通过云函数等服务端方式访问数据,服务端使用管理员权限不受 RLS 限制。

最佳实践

使用 DEFAULT 自动填充 user_id

建表时为 user_id 字段设置 DEFAULT auth.uid(),插入数据时数据库会自动填充当前用户的身份标识,无需客户端传值,避免伪造风险:

CREATE TABLE my_table (
id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
user_id TEXT NOT NULL DEFAULT auth.uid(),
content TEXT,
created_at TIMESTAMPTZ DEFAULT now()
);

这样客户端插入数据时无需指定 user_id,数据库会自动绑定当前登录用户:

// 无需传 user_id,数据库自动填充
const { error } = await db
.from("my_table")
.insert({ content: "hello world" });

配合 RLS 防止篡改

即使设置了 DEFAULT,恶意客户端仍可能在插入时显式传入伪造的 user_id。配合 INSERT 策略的 WITH CHECK 约束,可以确保写入的 user_id 必须与当前登录用户一致:

CREATE POLICY insert_own ON my_table
FOR INSERT
TO authenticated
WITH CHECK (user_id = auth.uid());

当客户端尝试传入他人的 user_id 时,该策略会拒绝写入。