SupaBase的接口使用比较灵活,可以在客户端直接调用,也可以在服务端调用。开发过web应用的同学在处理数据访问权限时应该比较熟悉一些基本的套路,甚至这些套路已经固化到了开发习惯中。我们以一个典型场景来说明一下传统Web开发过程中,数据鉴权的流程:
通常我们都会按照上面的思路来编写后端代码,校验用户是否有权限访问数据库中的某一条数据。同样的,在查询数据时,我们通常也是在后端程序中通过过滤条件来查询数据,以确保返回的数据是当前用户有权限访问的。
SupaBase提供了灵活的数据库访问接口,让开发者可以在前端代码中直接访问数据库中的数据,那么开发者能采用相同的模式在前端通过代码来实现类似的数据权限访问控制吗?
答案是:不能。
原因很直观。代码运行在客户端和服务端的差异非常明显,服务端代码是受保护的,用户无法篡改服务端程序的代码,因此在服务端代码中进行数据权限访问控制是可靠的。但是客户端不同,用户如果有一定的编码能力,可以随意改动客户端代码,尤其是像JavaScript这种脚本语言,代码都是明文的,更加方便修改。所以在客户端编写权限访问控制的代码逻辑是不合适的。所有的权限控制必须在服务端实现。
SupaBase解决这个问题的方案就是利用Postgrest RLS策略来实现数据访问控制,对于不熟悉Postgrest数据库的开发者,在阅读本篇内容之前,建议先阅读 【原理篇】SupaBase 权限模型part1 。
这是开发者比较关心的一个问题。简单回答:生产环境必须开RLS。
如果不开启RLS,数据库中的数据等于是对所有人开放的,任何人都可以通过接口对数据库中的所有数据进行增删查改操作,这显然不是开发者期望的结果。所以,使用Supabase开发应用时,只要上线就必须开启RLS。
你可能会说,如果该表中的数据是公开的,所有人都可以看的,不需要进行权限控制,这种表就可以不开启RLS。但事实上,即使是开放数据,开发者也通常不希望数据被任意篡改,所以生产环境下,即使是公开的数据表,通常也需要开启RLS,然后设置策略:运行任何人读取该表中的数据。
文章开头我们提到,开发者习惯于在代码中编写权限控制代码,这种习惯无比强大,以致于即使开启了RLS并配置了相关策略,也还是会习惯性的在客户端代码中增加权限控制的逻辑。 举一个例子:todos表定义如下:
create table todos (
id serial primary key,
user_id uuid,
task text,
......
)
用户A查询自己的TODS列表,开发者会不自然的写出如下代码:
const { data, error } = await supabase
.from('todos')
.select()
.eq('user_id', current_login_userid)
上面代码中,开发者通过'user_id'字段来过滤出当前登录用户的数据,然后在显示到UI界面上。这样写虽然没有问题,但事实上是没有必要的。开发者仍旧寄希望通过前端代码来控制返回的数据范围。
正确的写法是:
const { data, error } = await supabase
.from('todos')
.select()
同时,需要在数据库中配置如下策略
CREATE POLICY '用户只能访问自己的数据'
ON todos
USING (auth.uid() = user_id)
WITH CHECK (auth.uid() = user_id);
我们不自然的会产生一个疑问,数据访问控制全部在服务端通过RLS策略完成,那么过滤条件有什么用呢?客户端还有必要写过滤条件吗?
回答是:有。在客户端进行数据过滤主要是为了更精准的展示用户想看的数据,而不是为了权限控制。很多时候,用户的数据量是非常多的,这时候,用户可能只想看自己数据的一部分子集,比如用户只想看自己近三年的数学成绩,这种情况就需要在客户端编写过滤逻辑,只查询需要的数据。
也有同学会说,我可以把所有数据全部查出来,然后用js在客户端进行过滤。技术上没有问题,但性能会差一些,每次查询客户端和服务端要进行大量的数据通信。
答案:能。
SupaBase并没有限制用户的开发模式。SupaBase允许开发者在客户端直接访问数据库,这是SupaBase的优势,但开发者仍旧可以在服务端使用SupaBase的SDK开发应用。
当我们在服务端使用SupaBase开发应用时,主场又回到了开发者这一侧。我们可以在服务端编写任意的逻辑代码,不用担心这些逻辑被篡改。因此在服务端使用SupaBase时,开发者可以跟传统开发模式一样,通过程序来控制数据访问权限,不再依赖RLS策略。
需要注意的是,不依赖RLS策略,也需要开启RLS。原因前面解释过,如果不开启RLS,数据库中的数据任何人都可以随意操作。
开启RLS,但不配置任何策略,这样,客户端就没有任何权限操作数据库了。那服务端如何操作数据库呢?
这里就需要用到service_role key 。我们在开发客户端程序时,配置的是anon key 。anon key 是可以配置在客户端的,使用anon key 访问数据库会收到RLS策略的限制,而service_role key 不受RLS策略的约束,可以绕过RLS策略访问数据库中的任何数据。
所以,这里千万记住。service_role key 只能在服务端使用,不能泄露。