RLS 工作原理:行级别的安全门 要写好 RLS,必须先彻底理解它「行」级别的工作方式。本节讲清 RLS 的核心概念:什么是行级安全、如何启用与禁用、策略如何被评估,以及一个常被忽略的关键前提——默认拒绝。 从「表级权限」到「行级权限」 传统数据库权限控制往往停留在「表」的粒度:某个角色能读某张表、能写某张表。但这种粒度对多租户应用远远不够——用户表里所有用户的数据混在一张表,你要让每个用户只能看到自己的行,「表级」权限就无能为力。 行级安全(RLS) 正是为解决这个问题而生:它让权限判定下沉到「每一行」,根据当前请求者的身份,逐行决定这条数据是否可见、可改。于是「用户只能看自己的订单」「员工只能改本部门的资料」这类需求,都能用 RLS 精准表达。
要写好 RLS,必须先彻底理解它「行」级别的工作方式。本节讲清 RLS 的核心概念:什么是行级安全、如何启用与禁用、策略如何被评估,以及一个常被忽略的关键前提——默认拒绝。
传统数据库权限控制往往停留在「表」的粒度:某个角色能读某张表、能写某张表。但这种粒度对多租户应用远远不够——用户表里所有用户的数据混在一张表,你要让每个用户只能看到自己的行,「表级」权限就无能为力。
行级安全(RLS) 正是为解决这个问题而生:它让权限判定下沉到「每一行」,根据当前请求者的身份,逐行决定这条数据是否可见、可改。于是「用户只能看自己的订单」「员工只能改本部门的资料」这类需求,都能用 RLS 精准表达。
RLS 是针对每张表启用的开关。一张表可以选择:
极其重要:新建的表默认未启用 RLS。在 Supabase 中,未启用 RLS 的表若被客户端直接访问,等同于完全公开——这是最常见、也最危险的安全疏漏。每张承载真实数据的业务表,都应第一时间启用 RLS。
启用 RLS 是一条简单的开关语句(针对某张表)。启用后,你可以为该表编写多条策略。禁用 RLS 同样是一条语句,但生产环境中几乎不应禁用已启用的 RLS——那等于拆掉守门人。
启用 RLS 后,真正决定访问规则的是策略(policy)。一条策略本质上是:
例如「查询时,只允许读取 user_id 等于当前用户 id 的行」,就是一条针对 SELECT 的策略。
理解策略如何被联合评估,是写对 RLS 的关键:
这条「默认拒绝」原则是 RLS 安全性的根基:宁可全拒,不可误放。
策略表达式需要知道「当前是谁」。PostgreSQL 在会话中维护着当前用户与角色信息,Supabase 则把 JWT 中的身份映射进来,提供一组函数供策略调用,例如:
策略通过这些函数拿到身份后,再与每一行的字段(如 user_id)比较,从而做出「这一行属于当前用户吗」的判断。具体函数名与用法见下一节。
有一个重要例外:使用服务级密钥(service role key)的连接可以绕过 RLS。这个角色用于可信的后端环境(如边缘函数),需要直接操作所有数据时使用。
务必牢记:服务级密钥拥有绕过一切行级安全的特权,绝不能出现在前端代码或公开仓库中。前端只能使用受 RLS 保护的匿名密钥(anon key)配合用户令牌访问数据。
理解了 RLS,你就明白 Supabase 的架构为何如此独特:
RLS 让数据库本身承担了鉴权职责,前端因此可以安全直连,省去自建后端这一整层。这大幅简化了架构,也减少了鉴权逻辑在多处实现不一致的风险。
RLS 是表级的行级安全开关,默认未启用需主动开启;启用后由策略逐行评估,遵循「默认拒绝」原则;当前用户身份来自 JWT;服务级密钥可绕过 RLS。掌握这些原理,下一节我们就能动手编写策略,把规则精确地表达出来。