基础权限
CloudBase PostgreSQL 数据库通过 行级安全策略(Row Level Security,RLS) 实现基础数据权限控制。
RLS 允许你在数据库层面定义访问策略,精确控制每一行数据的读写权限,确保用户只能访问被授权的数据。
RLS 机制简介
PostgreSQL 的 RLS 机制通过在表上创建安全策略(Policy)来控制行级别的数据访问。其核心流程为:
- 启用 RLS:对目标表开启行级安全策略
- 创建策略:定义允许用户执行哪些操作(SELECT、INSERT、UPDATE、DELETE)以及可访问哪些行
- 自动过滤:数据库在执行查询时,自动根据策略过滤数据行,对应用层完全透明
在 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 时,该策略会拒绝写入。