Skip to content
Realtime

实时授权

你可以通过在 realtime.messages 表上添加行级安全策略来控制客户端对 Realtime BroadcastPresence 的访问。每个 RLS 策略可以对应客户端可以执行的特定操作:

🌐 You can control client access to Realtime Broadcast and Presence by adding Row Level Security policies to the realtime.messages table. Each RLS policy can map to a specific action a client can take:

  • 控制哪些客户端可以向通道广播
  • 控制哪些客户端可以接收通道的广播
  • 控制哪些客户端可以向通道发布他们的状态
  • 控制哪些客户端可以接收关于其他客户端状态的消息

它是怎么运作的 #

🌐 How it works

实时功能使用你数据库中 realtime 模式下的 messages 表,在客户端连接到通道主题时生成访问策略。

🌐 Realtime uses the messages table in your database's realtime schema to generate access policies for your clients when they connect to a Channel topic.

通过在 realtime.messages 表上创建 RLS 策略,你可以控制用户对通道主题以及通道主题中的功能的访问权限。

🌐 By creating RLS policies on the realtime.messages table you can control the access users have to a Channel topic, and features within a Channel topic.

当用户连接时会进行验证。当他们的 WebSocket 连接建立并加入一个通道主题时,会根据以下内容计算他们的权限:

🌐 The validation is done when the user connects. When their WebSocket connection is established and a Channel topic is joined, their permissions are calculated based on:

  • realtime.messages 表上的 RLS 策略
  • 作为他们的 Auth JWT 一部分发送的用户信息
  • 请求头
  • 用户尝试连接的通道话题

当 Realtime 为客户生成策略时,它会对 realtime.messages 表执行查询,然后回滚。Realtime 不会在你的 realtime.messages 表中存储任何消息。

🌐 When Realtime generates a policy for a client it performs a query on the realtime.messages table and then rolls it back. Realtime does not store any messages in your realtime.messages table.

使用实时授权涉及两个步骤:

🌐 Using Realtime Authorization involves two steps:

  • 在你的数据库里,在 realtime.messages 上创建 RLS 策略
  • 在你的客户端中,用 config 选项 private: true 实例化实时通道

正在访问请求信息 #

🌐 Accessing request information

realtime.topic#

在编写 RLS 策略时,你可以使用 realtime.topic 辅助函数。它会返回用户尝试连接的通道主题。

🌐 You can use the realtime.topic helper function when writing RLS policies. It returns the Channel topic the user is attempting to connect to.

1
create policy "authenticated can read all messages on topic"
2
on "realtime"."messages"
3
for select
4
to authenticated
5
using (
6
(select realtime.topic()) = 'room-1'
7
);

JWT 声明 #

🌐 JWT claims

用户声明可以通过 current_setting 函数访问。这些声明以 JSON 对象的形式在 request.jwt.claims 设置中可用。

🌐 The user claims can be accessed using the current_setting function. The claims are available as a JSON object in the request.jwt.claims setting.

1
create policy "authenticated with supabase.io email can read all"
2
on "realtime"."messages"
3
for select
4
to authenticated
5
using (
6
-- Only users with the email claim ending with @supabase.io
7
(((current_setting('request.jwt.claims'))::json ->> 'email') ~~ '%@supabase.io')
8
);

示例 #

🌐 Examples

以下示例使用这个模式:

🌐 The following examples use this schema:

1
create table public.rooms (
2
id bigint generated by default as identity primary key,
3
topic text not null unique
4
);
5
6
GRANT SELECT ON public.rooms TO anon;
7
8
alter table public.rooms enable row level security;
9
10
create table public.profiles (
11
id uuid not null references auth.users on delete cascade,
12
email text NOT NULL,
13
14
primary key (id)
15
);
16
17
GRANT SELECT ON public.profiles TO anon;
18
GRANT SELECT, INSERT, UPDATE, DELETE ON public.profiles TO authenticated;
19
20
alter table public.profiles enable row level security;
21
22
create table public.rooms_users (
23
user_id uuid references auth.users (id),
24
room_topic text references public.rooms (topic),
25
created_at timestamptz default current_timestamp
26
);
27
28
GRANT SELECT ON public.rooms_users TO authenticated;
29
30
alter table public.rooms_users enable row level security;
31
32
create policy "authenticated can read own room memberships"
33
on public.rooms_users
34
for select
35
to authenticated
36
using ((select auth.uid()) = user_id);

广播 #

🌐 Broadcast

realtime.messages 表中的 extension 字段记录消息类型。对于广播消息,realtime.messages.extension 的值是 broadcast。你可以在 RLS 策略中检查这个情况。

🌐 The extension field on the realtime.messages table records the message type. For Broadcast messages, the value of realtime.messages.extension is broadcast. You can check for this in your RLS policies.

允许用户加入(并阅读)广播主题 #

🌐 Allow a user to join (and read) a Broadcast topic

要加入一个广播通道,用户至少需要对该通道话题拥有一个读或写权限。

🌐 To join a Broadcast Channel, a user must have at least one read or write permission on the Channel topic.

在这里,我们允许与关系表 public.room_users 中请求的主题相关联的用户进行读取(select):

🌐 Here, we allow reads (selects) for users who are linked to the requested topic within the relationship table public.room_users:

1
create policy "authenticated can receive broadcast"
2
on "realtime"."messages"
3
for select
4
to authenticated
5
using (
6
exists (
7
select
8
user_id
9
from
10
rooms_users
11
where
12
user_id = (select auth.uid())
13
and room_topic = (select realtime.topic())
14
and realtime.messages.extension in ('broadcast')
15
)
16
);

然后,要加入一个启用了 RLS 的话题,需要在实例化 Channel 时将 private 选项设置为 true

🌐 Then, to join a topic with RLS enabled, instantiate the Channel with the private option set to true.

1
import { createClient } from '@supabase/supabase-js'
2
const supabase = createClient('your_project_url', 'your_supabase_api_key')
3
4
// ---cut---
5
const channel = supabase.channel('room-1', {
6
config: { private: true },
7
})
8
9
channel
10
.on('broadcast', { event: 'test' }, (payload) => console.log(payload))
11
.subscribe((status, err) => {
12
if (status === 'SUBSCRIBED') {
13
console.log('Connected!')
14
} else {
15
console.error(err)
16
}
17
})

允许用户发送广播消息 #

🌐 Allow a user to send a Broadcast message

要授权发送广播消息,为 insert 创建一个策略,其中 realtime.messages.extension 的值为 broadcast

🌐 To authorize sending Broadcast messages, create a policy for insert where the value of realtime.messages.extension is broadcast.

在这里,我们允许与关系表 public.room_users 中请求的主题相关联的用户进行写入(发送)操作:

🌐 Here, we allow writes (sends) for users who are linked to the requested topic within the relationship table public.room_users:

1
create policy "authenticated can send broadcast on topic"
2
on "realtime"."messages"
3
for insert
4
to authenticated
5
with check (
6
exists (
7
select
8
user_id
9
from
10
rooms_users
11
where
12
user_id = (select auth.uid())
13
and room_topic = (select realtime.topic())
14
and realtime.messages.extension in ('broadcast')
15
)
16
);

存在 #

🌐 Presence

realtime.messages 表上的 extension 字段记录消息类型。对于 Presence 消息,realtime.messages.extension 的值是 presence。你可以在你的 RLS 策略中检查这个。

🌐 The extension field on the realtime.messages table records the message type. For Presence messages, the value of realtime.messages.extension is presence. You can check for this in your RLS policies.

允许用户在通道上收听在线状态消息 #

🌐 Allow users to listen to Presence messages on a Channel

select制定关于realtime.messages的政策,其中realtime.messages.extensionpresence

🌐 Create a policy for select on realtime.messages where realtime.messages.extension is presence.

1
create policy "authenticated can listen to presence in topic"
2
on "realtime"."messages"
3
for select
4
to authenticated
5
using (
6
exists (
7
select
8
user_id
9
from
10
rooms_users
11
where
12
user_id = (select auth.uid())
13
and room_topic = (select realtime.topic())
14
and realtime.messages.extension in ('presence')
15
)
16
);

允许用户在通道上发送在线状态消息 #

🌐 Allow users to send Presence messages on a channel

要更新用户的状态,请在 realtime.messages 上为 insert 创建一个策略,其中 realtime.messages.extension 的值为 presence

🌐 To update the Presence status for a user create a policy for insert on realtime.messages where the value of realtime.messages.extension is presence.

1
create policy "authenticated can track presence on topic"
2
on "realtime"."messages"
3
for insert
4
to authenticated
5
with check (
6
exists (
7
select
8
user_id
9
from
10
rooms_users
11
where
12
user_id = (select auth.uid())
13
and room_topic = (select realtime.topic())
14
and realtime.messages.extension in ('presence')
15
)
16
);

出席和广播 #

🌐 Presence and Broadcast

通过在 where 过滤器中包含两个扩展,授权同时使用 Presence 和 Broadcast。

🌐 Authorize both Presence and Broadcast by including both extensions in the where filter.

广播和在场读取 #

🌐 Broadcast and Presence read

在一个 RLS 策略中授权读取权限和广播。

🌐 Authorize Presence and Broadcast read in one RLS policy.

1
create policy "authenticated can listen to broadcast and presence on topic"
2
on "realtime"."messages"
3
for select
4
to authenticated
5
using (
6
exists (
7
select
8
user_id
9
from
10
rooms_users
11
where
12
user_id = (select auth.uid())
13
and room_topic = (select realtime.topic())
14
and realtime.messages.extension in ('broadcast', 'presence')
15
)
16
);

广播和在场写 #

🌐 Broadcast and Presence write

在一个 RLS 策略中授权 Presence 和 Broadcast 写入。

🌐 Authorize Presence and Broadcast write in one RLS policy.

1
create policy "authenticated can send broadcast and presence on topic"
2
on "realtime"."messages"
3
for insert
4
to authenticated
5
with check (
6
exists (
7
select
8
user_id
9
from
10
rooms_users
11
where
12
user_id = (select auth.uid())
13
and room_topic = (select realtime.topic())
14
and realtime.messages.extension in ('broadcast', 'presence')
15
)
16
);

与 Postgres 的互动变更 #

🌐 Interaction with Postgres Changes

当在启用了 RLS 的表上使用 Postgres Changes 时,数据库记录只会发送给根据你的 RLS 策略被允许读取的客户端。

🌐 When using Postgres Changes on tables with RLS, database records are sent only to clients who are allowed to read them based on your RLS policies.

私有和公共通道都可以订阅 Postgres 的变更。

🌐 Private and public channels can subscribe to Postgres Changes.

更新 RLS 策略 #

🌐 Updating RLS policies

客户端访问策略会在连接期间被缓存。每条通道消息不会都去查询你的数据库。

🌐 Client access policies are cached for the duration of the connection. Your database is not queried for every Channel message.

当以下情况发生时,Realtime 会根据你的 RLS 策略实时更新客户端的访问策略缓存:

🌐 Realtime updates the access policy cache for a client based on your RLS policies when:

  • 一个客户端连接到实时并订阅一个通道
  • 一个新的 JWT 会通过客户端通过 access_token 消息 发送到 Realtime

如果在通道上从未收到新的 JWT,当 JWT 过期时,客户端将会断开连接。

🌐 If a new JWT is never received on the Channel, the client will be disconnected when the JWT expires.

确保把 JWT 的过期时间窗口设置得短一些。

🌐 Make sure to keep the JWT expiration window short.