策略设计与编写:把规则精确表达出来 理解原理后,本节进入策略的具体编写。你将学会策略的语法结构、如何针对不同命令(查询/插入/更新/删除)编写策略,以及几个关键的用户身份函数。 策略的基本结构 一条策略通常包含以下要素: 名称:便于识别策略意图,如「用户只能读自己的行」。 作用的表:策略挂在哪张表上。 覆盖的命令:SELECT / INSERT / UPDATE / DELETE / ALL(全部)。 针对的角色:哪些角色适用(匿名、认证用户、或全部)。 布尔表达式:决定某行是否放行的核心逻辑。 概念上,一条策略可读作:「对于在某表上执行某命令的某角色,当布尔表达式为真时,允许操作这一行」。 关键身份函数 策略表达式中,最常用的是几个读取当前用户身份的函数。
理解原理后,本节进入策略的具体编写。你将学会策略的语法结构、如何针对不同命令(查询/插入/更新/删除)编写策略,以及几个关键的用户身份函数。
一条策略通常包含以下要素:
概念上,一条策略可读作:「对于在某表上执行某命令的某角色,当布尔表达式为真时,允许操作这一行」。
策略表达式中,最常用的是几个读取当前用户身份的函数。这里用通用概念描述它们的语义(具体函数名以 Supabase 提供的为准):
user_id 列与它比较。掌握这三个函数,就能表达绝大多数策略。
最常见的策略。表达式决定某行是否出现在查询结果中。
语义示例:「只返回 user_id 等于当前用户 id 的行」。
-- 概念示意:允许认证用户查询属于自己的行 CREATE POLICY "用户只能读自己的行" ON orders FOR SELECT TO authenticated USING (user_id = auth.uid());
USING 子句对每行评估,为真则该行可见。
INSERT 策略防止用户插入「不属于自己」的数据。它有一个特别的检查子句(WITH CHECK),在插入时对新行求值。
语义示例:「插入订单时,新行的 user_id 必须等于当前用户 id」。
CREATE POLICY "用户只能为自己下单" ON orders FOR INSERT TO authenticated WITH CHECK (user_id = auth.uid());
INSERT 策略极其重要——否则用户 A 可以往订单里写 user_id 为用户 B 的行,冒充他人。务必让
WITH CHECK锁定 user_id 与当前用户一致。
UPDATE 策略既要控制「能改哪些已存在的行」,又要控制「改成什么样」。因此它有两个子句:
USING:哪些旧行允许被更新。WITH CHECK:更新后的新行必须满足什么。语义示例:「用户只能更新自己的行,且更新后 user_id 仍必须是本人」。
CREATE POLICY "用户只能改自己的行" ON orders FOR UPDATE TO authenticated USING (user_id = auth.uid()) WITH CHECK (user_id = auth.uid());
两层检查确保用户既能改自己的行,又不能借更新把数据「转移」给别人。
DELETE 策略只关心 USING:哪些行允许被删除。
CREATE POLICY "用户只能删自己的行" ON orders FOR DELETE TO authenticated USING (user_id = auth.uid());
如果想用同一条规则覆盖所有命令,可用 FOR ALL,并在 USING 与 WITH CHECK 中分别表达读改与写入条件。适合规则统一的简单表。
策略还要指明对哪些角色生效:
anon:匿名用户(未登录)。authenticated:已登录用户。public:所有角色(含匿名与认证)。例如公开文章表,读取策略可用 TO public(所有人可读),写入策略用 TO authenticated(只有登录用户能写)。这种按角色分层是常见做法。
回顾几个要点:
测试是关键。策略写错往往不会报错,而是悄悄放行或误拒。养成「写完必测」的习惯。
策略由命令、角色与布尔表达式组成,通过 USING 与 WITH CHECK 控制读写;身份函数提供当前用户 id、登录状态与角色;INSERT/UPDATE 的 WITH CHECK 尤为关键,能防止冒名写入。下一节我们把这些策略与第 4 章的用户身份结合,实现真正的数据隔离。