作为前端工程师,行级安全策略(RLS)这个概念可能大家会有点陌生,不过没关系,一般情况下,大家估计也用不到。但是,如果你要开始思考开发一款完整的应用。了解他就显得很有必要了。
如果我们想要快速开发一个个人产品,但是又不想花太多的精力去配置数据库、管理身份校验、维护后端服务等等,那么 BaaS 平台就是非常适合我们的选择之一。
BaaS,Backend as a Service,后端及服务。
Supabase 是比较火的 BaaS 平台之一。在使用 Supabase 的过程中,我们可以为创建的数据表设置对应的行级安全策略,用于约束不同的访问规则。
RLS
行级安全,Row Level Security。作为项目管理员,我们可以单独为数据表创建访问策略。当接口访问数据库时,需要先确保这些策略验证通过之后,才能正常访问

如上图所示,Supabase 中,在创建好的数据表头部有一个按钮 Add RSL policy
,点击该按钮,我们就可以添加对应的行级安全策略

然后点击 Create Policy,会出现如下创建页面。

在创建页面的右侧,有已经配置好的一些模板,如果对语法不熟悉,可以通过参考模板,并结合自己的需求来调整安全策略。
例如,针对用户表 users,我们可以约定一条规则,仅允许用户自己修改自己的那一条用户数据。配置的策略如下所示
create policy "Users can update their own profiles."
on users
for update
using (
auth.uid() = id
);
1、create policy: Users...
第一行,是一句备注说明:用户可以修改自己的个人信息。
2、on users:
users 是数据表的名称,表示该策略将会用于名为 users 的数据表。
3、for update:
表示该策略仅针对于更新操作生效
4、using:
表示一个业务级别的判断条件,例如这里的判断条件是 auth.uid() = id
,表示当前的访问用户的 uid
需要与表中字段 id
的值相等,才能符合安全策略。
Policy Behavior
操作一个表有几种行为:SELECT
、INSERT
、UPDATE
、DELETE
、ALL
我们可以针对同一个表、同一个表的同一个行为配置多个安全策略。那么接下来要思考的一个问题就是,多个安全策略我们需要同时都验证通过才能操作数据呢,还是只满足一个安全策略就可以了?

Supabase 提供了一个配置,让我们可以设置具体选择哪一种。其中有两个值
// OR
as PERMISSIVE
// AND
as RESTRICTIVE
Roles
Supabase 通过提供了一组预定义角色来增强安全策略的访问控制。在配置代码中,可以通过 to authenticated
来配置
create policy "Enable insert for authenticated users only"
on "public"."vip"
as PERMISSIVE
for INSERT
+ to authenticated
with check (
true
);
有如下预定义角色
postgres
: 具有完全权限的默认管理员角色
anon
: 匿名用户,没有用户登录时访问数据库
authenticator
: 这是一个特殊角色,访问权限非常优先。他基于 token 验证 JTWs 并解析出另外一个角色,例如,下面的对象是 JTWs 解码之后的结果,此时包含的角色为 anon
。
{
"role": "anon",
"iss": "supabase",
"iat": 1614205174,
"exp": 1929781174
}
authenticated
: 表示 API 请求中,已经登录的用户
service_role
: 绕过行级安全性来提升 API 访问权限。
supabase_auth_admin
: 在迁移期间管理身份验证模式。访问范围仅限于 auth
架构。
supabase_storage_admin
: 在迁移期间管理存储模式。访问范围仅限于 storage
架构。
dashboard_user
: 通过 Supabase UI 执行命令。
supabase_admin
: 数据库维护的管理角色。
除此之外,我们还可以通过 SQL
命令直接在 PostgreSQL 中创建新的角色
create role "new_role";
并且授予该角色特定的权限
grant select on table example_table to new_role;

运行成功之后,在安全策略的配置中,就可以选择刚才创建好的角色。

using | with check
有的时候,我们会看到模板里面会出现 with check
语法。如下所示。
create policy "Enable update for users based on email"
on "public"."vip"
as PERMISSIVE
for UPDATE
to public
using (
(select auth.jwt()) ->> 'email' = email
with check (
(select auth.jwt()) ->> 'email' = email
);
这里的他的作用与 using
是类似的,都是表示执行一个判断条件。不过他们针对的数据不一样。using
针对数据表中已经存在的数据。而 with check
则针对新加入到数据表中的数据。
因此,with check
通常用于 INSERT、UPDATE
这样的可能会存在新加入数据表数据的行为中。
性能优化
行级安全策略虽然能够更大程度上保护数据的可见性,但是在执行的过程中,如果处理不当,依然有可能会造成不小的性能问题,导致数据访问速度过慢。因此,在编写安全策略的过程中,我们需要考虑如何以最小的成本来优化执行过程。
例如下面这个案例
create policy "rls_test_select"
on test_table
to authenticated
using ( auth.uid() = user_id );
这里在执行 using
判断时,我们使用了 auth.uid()
方法,一个比较好的方式,就是多次访问时,我们可以缓存他的结果,从而加快后续的比较速度
我们可以稍微调整就可以做到想要的效果
create policy "rls_test_select"
on test_table
to authenticated
using ( (select auth.uid()) = user_id );
只需要在 auth.uid()
前面添加上 select
即可。auth.jwt()
也可以用这种方式来优化。
另外一个优化策略,则是使用 B-TREE 建立索引的方式来加快查找速度。
create policy "rls_test_select"
on test_table
to authenticated
using ( (select auth.uid()) = user_id );
添加 B-TREE 索引
create index userid
on test_table
using btree (user_id);
优化之后的速度会得到显著的提升

总结
针对数据库设计合理的行级安全策略,是管理数据安全的一种必备的手段。只是可能我们前端很少会接触到这些东西,所以整个内容会比较陌生。但是如果我们对 sql 语句有所学习的话,那么这些内容就非常的基础。作为前端开发在适当的时候也应该了解一些
- END -
如果您关注前端+AI 相关领域可以扫码进群交流
添加小编微信进群😊
关于奇舞团
奇舞团是 360 集团最大的大前端团队,非常重视人才培养,有工程师、讲师、翻译官、业务接口人、团队 Leader 等多种发展方向供员工选择,并辅以提供相应的技术力、专业力、通用力、领导力等培训课程。奇舞团以开放和求贤的心态欢迎各种优秀人才关注和加入奇舞团。