supabase是什么?
一个BaaS,在postgres上增加api层,为每个表生成api。并且支持 edge function(serverless函数调用),storage (对象存储),认证等
管理项目
创建一个Project,填写基本信息,包括数据库密码,选择Region

创建好之后,到首页可以看到一个URL和API KEY,前端将通过他来调用 supabase的提供的data api对数据库进行增删改查。
下面的小字说,这个key是可以被公开的,那么supabase是如何确保数据库操作的安全呢?答案是通过RLS实现的行级别的鉴权,这个后面会说到。

认证
supabase提供了开箱即用的认证功能,可以不写一行代码实现用户注册、登录等。
到 API docs -- User Managerment可以看到如何用去注册一个用户

我们命令行复制 并执行:

发现api成功返回了,然后到Table Editor中auth.users中可以看到,多了一条用户记录

这里schema有多种,其中auth是supabase直接管理的,用来实现认证,用户不能进行修改
用户创建的表在public下,创建完之后 supabase 会自动生成增删改查的api
建表
从上一部分可以看到,auth.users 并不存在 用户名称、简介等业务信息,就需要我们自己在public下创建一个users表,然后使用外键建立关联

设置外键,将public.users.uid 指向 auth.users.id ,并且设置当 auth.users记录被删除时,同步删除业务表的记录

trigger function
现在发现了个问题,当用户注册的时候,auth.users 会多一条记录,但是public.users却没有,难道还需要我们登录之后手动调用insert创建吗?
其实不用,我们可以使用supabase的trigger function,auth.users改变时触发,执行sql给public.users插入一条记录
到 Database -> function, 在public创建一个function,其实就是执行了一段sql,其中 new 代表auth.users中的一条记录,raw_user_meta_data 则是调用接口时传入的业务对象,这个现在看起来有点懵逼,后面会说到
sql
-- 创建函数
CREATE OR REPLACE FUNCTION public.handle_new_user()
RETURNS TRIGGER AS $$
BEGIN
INSERT INTO public.users (uid, username)
VALUES (
NEW.id,
NEW.raw_user_meta_data->>'username',
);
RETURN NEW;
END;
$$ LANGUAGE plpgsql SECURITY DEFINER;
有了执行逻辑,还要定义什么时候触发,到database -> triggers, 在public下新增一个trigger,指定到auth.users表插入的时候执行对应的trigger:
sql
-- 创建触发器
CREATE TRIGGER on_auth_user_created
AFTER INSERT ON auth.users
FOR EACH ROW EXECUTE FUNCTION public.handle_new_user();

然后删除刚刚的数据,
sql
delete from auth.users where id = '06ca9840-a63f-447e-96af-e7f3ff768342'
重新注册一下,注意这里data对象就是上面函数中的raw_user_meta_data:

再查看一下业务表 public.users,多了一条记录

RLS: Row Level Security
如果想对某些行进行鉴权,比如
- 用户只能修改自己的信息
- 任何人都可以创建post
- 任何人只能查看自己的post
就需要用到RLS,通过创建polices来控制,官方的例子很具体了,这里解释一下
vbnet
create policy "Public profiles are viewable only by authenticated users"
on profiles for select
to authenticated
using ( true );
表示创建了一个规则,对于profiles的select操作,对登录用户进行限制,限制为true,就是任何登录用户都可以查看profiles
sql
create policy "Users can update their own profile."
on profiles for update
to authenticated -- the Postgres Role (recommended)
using ( (select auth.uid()) = user_id ) -- checks if the existing row complies with the policy expression
with check ( (select auth.uid()) = user_id ); -- checks if the new row complies with the policy expression
这里 auth.uid() 是从读取请求中的jwt解析得到的人当前登录用户的id,user_id是表中的列
这个polices限制了,对于profile的更新操作,只有登录用户,可以查看到自己的(using),并且修改后id不能变(with check),没有with check的话,用户可以把自己的profile中的uid改成别人的
这里区分一下using和with check的区别:
逻辑 | 含义 |
---|---|
USING |
能不能选中这行(决定可操作范围) |
WITH CHECK |
改完后是否合法(防止越权写入) |