关于

如何将表的访问限制在认证用户、行级策略和基于电子邮件域的访问。

基于用户的行级策略

现在我们知道了如何根据JWT角色来限制对表的访问,我们可以把它和用户管理结合起来,让我们对用户可以从数据库中读取和写入哪些数据有更多的控制。

我们将从用户会话在supabase的工作方式开始,然后转到编写以用户为中心的策略。

比方说,我们要把一个用户第一次注册到我们的服务中。典型的方法是在supabase-js中调用以下方法:

// see full api reference here: https://supabase.io/docs/reference/javascript/auth-signup
supabase.auth.signUp({ email, password })

默认情况下,这将向用户发送一封确认电子邮件。当用户点击电子邮件中的链接时,他们将被重定向到你的网站.你需要在仪表板上的授权>设置中提供你的网站网址。
默认情况下,这是http://localhost:3000, 包括查询参数的完整URL将看起来像这样:

http://localhost:3000/#access_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJhdXRoZW50aWNhdGVkIiwiZXhwIjoxNjE2NDI5MDY0LCJzdWIiOiI1YTQzNjVlNy03YzdkLTRlYWYtYThlZS05ZWM5NDMyOTE3Y2EiLCJlbWFpbCI6ImFudEBzdXBhYmFzZS5pbyIsImFwcF9tZXRhZGF0YSI6eyJwcm92aWRlciI6ImVtYWlsIn0sInVzZXJfbWV0YWRhdGEiOnt9LCJyb2xlIjoiYXV0aGVudGljYXRlZCJ9.4IFzn4eymqUNYYo2AHLxNRL8m08G93Qcg3_fblGqDjo&expires_in=3600&refresh_token=RuioJv2eLV05lgH5AlJwTw&token_type=bearer&type=signup

让我们把它分开,以便更容易阅读:

// the base url - whatever you set in the Auth Settings in app.supabase.io dashboard
http://localhost:3000/

// note we use the '#' (fragment) instead of '?' query param
// the access token is a JWT issued to the user
#access_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJhdXRoZW50aWNhdGVkIiwiZXhwIjoxNjE2NDI5MDY0LCJzdWIiOiI1YTQzNjVlNy03YzdkLTRlYWYtYThlZS05ZWM5NDMyOTE3Y2EiLCJlbWFpbCI6ImFudEBzdXBhYmFzZS5pbyIsImFwcF9tZXRhZGF0YSI6eyJwcm92aWRlciI6ImVtYWlsIn0sInVzZXJfbWV0YWRhdGEiOnt9LCJyb2xlIjoiYXV0aGVudGljYXRlZCJ9.4IFzn4eymqUNYYo2AHLxNRL8m08G93Qcg3_fblGqDjo

// valid for 60 minutes by default
&expires_in=3600

// use to get a new access_token before 60 minutes expires
&refresh_token=RuioJv2eLV05lgH5AlJwTw

// can use as the Authorization: Bearer header in requests to your API
&token_type=bearer

// why was this token issued? was it a signup, login, password reset, or magic link?
&type=signup

如果我们把access_token放入https://jwt.io,我们会看到它被解码为:

{
  "aud": "authenticated",
  "exp": 1616429064,
  "sub": "5a4365e7-7c7d-4eaf-a8ee-9ec9432917ca",
  "email": "ant@supabase.io",
  "app_metadata": {
    "provider": "email"
  },
  "user_metadata": {},
  "role": "authenticated"
}

authenticated角色在Supabase中很特别,它告诉API这是一个已认证的用户,并知道将JWT与你添加到请求的资源(表或行)的任何政策进行比较。

sub要求通常是我们用来将JWT与你数据库中的行相匹配的,因为默认情况下,它是auth.users表中用户的唯一标识符(作为附带说明,一般不建议以任何方式改变MemFire Cloud数据库中的auth模式,因为Auth API依靠它来正常运行)。

对于好奇心强的人来说,可以尝试进入SQL编辑器并进行查询。

select * from auth.users;

如果supabase-js在你的网站上被加载, 本例中为http://localhost:3000, 那么它将自动从URL中提取access_token,并启动一个会话。
你可以检查 session() 方法,看看是否有一个有效的会话:

console.log(supabase.auth.session())

现在我们可以使用supabase.auth.signIn({ email, password})这样的方法来向用户发布JWTs,我们想开始获取特定于该用户的资源。因此,让我们做一些。 转到SQL编辑器,运行。

create table my_scores (
    name text,
    score int,
    user_id uuid not null
);

ALTER TABLE my_scores ENABLE ROW LEVEL SECURITY;

insert into my_scores(name, score, user_id)
values
  ('Paul', 100, '5a4365e7-7c7d-4eaf-a8ee-9ec9432917ca'),
  ('Paul', 200, '5a4365e7-7c7d-4eaf-a8ee-9ec9432917ca'),
  ('Leto', 50,  '9ec94326-2e2d-2ea2-22e3-3a535a4365e7');

-- use UUIDs from the auth.users table if you want to try it
-- for yourself

现在我们要写我们的策略,还是用SQL,但注意也可以通过仪表盘上的数据库管理->权限配置来添加:

CREATE POLICY user_update_own_scores ON my_scores
    FOR ALL
    USING (auth.uid() = user_id);

现在,假设你在你的javascript/supabase-js环境中有一个活动的会话,你可以做:

supabase.from('my_scores').select('*').then(console.log)

而你应该只收到属于当前登录用户的分数。另外,你也可以使用Bash,比如:

curl 'https://sjvwsaokcugktsdaxxze.supabase.co/rest/v1/my_scores?select=*' \
-H "apikey: <ANON_KEY>" \
-H "Authorization: Bearer <ACCESS_TOKEN>"

请注意,要通过API网关,总是需要anon key(或service role key)。这可以在 apikey头或名为 apikey的查询参数中传递。只要你用它来实例化客户端,它就会在supabase-js中自动传递。

这里还有一些关于如何构建你的模式以最好地与auth.users表整合的说明。

一旦你掌握了策略的窍门,你就可以开始有点花哨了。比方说,我在暴雪工作,我只希望暴雪的工作人员能够更新人们的高分,我可以写一些东西:

create or replace function auth.email() returns text as $$
  select nullif(current_setting('request.jwt.claim.email', true), '')::text;
$$ language sql;

create policy "Only Blizzard staff can update leaderboard"
  on my_scores
  for update using (
    right(auth.email(), 13) = '@blizzard.com'
  );

Supabase有三个内置的辅助函数。auth.email(), auth.uid()auth.role()

在这里可以看到完整的PostgreSQL策略文档

你可以随心所欲地使用这些策略。

资源

接下来的步骤

results matching ""

    No results matching ""