实时

Realtime 授权管理


您可以通过向 realtime.messages 表添加行级安全策略(RLS)来控制客户端对实时广播在线状态的访问权限。每个RLS策略可以映射到客户端可执行的特定操作:

  • 控制哪些客户端可以向频道广播
  • 控制哪些客户端可以接收来自频道的广播
  • 控制哪些客户端可以发布自己的在线状态到频道
  • 控制哪些客户端可以接收其他客户端的在线状态消息

工作原理

Realtime 功能通过数据库 realtime 模式中的 messages 表,在客户端连接到频道主题时为其生成访问策略。

通过在 realtime.messages 表上创建 RLS (行级安全) 策略,您可以控制用户对频道主题及其内部功能的访问权限。

验证过程在用户连接时完成。当 WebSocket 连接建立并加入频道主题时,系统会根据以下因素计算用户权限:

  • realtime.messages 表上的 RLS 策略
  • 用户 Auth JWT 中包含的用户信息
  • 请求头信息
  • 用户尝试连接的频道主题

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

使用 Realtime 授权功能包含两个步骤:

  1. 在数据库中为 realtime.messages 创建 RLS 策略
  2. 在客户端使用 config 选项 private: true 实例化 Realtime 频道

辅助函数

在编写 RLS 策略时,您可以使用以下辅助函数:

realtime.topic

返回用户尝试连接的频道主题。

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

示例

以下示例使用以下数据库模式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
create table public.rooms ( id bigint generated by default as identity primary key, topic text not null unique);alter table public.rooms enable row level security;create table public.profiles ( id uuid not null references auth.users on delete cascade, email text NOT NULL, primary key (id));alter table public.profiles enable row level security;create table public.rooms_users ( user_id uuid references auth.users (id), room_topic text references public.rooms (topic), created_at timestamptz default current_timestamp);alter table public.rooms_users enable row level security;

广播消息

realtime.messages 表中的 extension 字段记录了消息类型。对于广播消息,realtime.messages.extension 的值为 broadcast。您可以在 RLS (行级安全) 策略中检查此值。

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

要加入广播频道,用户必须至少拥有该频道主题的读取或写入权限。

在此示例中,我们允许已关联到关系表 public.room_users 中指定主题的用户进行读取操作(select):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
create policy "已认证用户可接收广播"on "realtime"."messages"for selectto authenticatedusing (exists ( select user_id from rooms_users where user_id = (select auth.uid()) and topic = (select realtime.topic()) and realtime.messages.extension in ('broadcast') ));

然后,要在启用行级安全(RLS)的情况下加入主题,需将频道的 private 选项设置为 true 来实例化频道。

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

允许用户发送广播消息

要授权发送广播消息,需要为 insert 操作创建一个策略,条件是 realtime.messages.extension 的值为 broadcast

以下示例允许已认证用户在关联表 public.room_users 中与请求主题相关联时进行写入(发送)操作:

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

在线状态(Presence)

realtime.messages 表中的 extension 字段记录消息类型。对于在线状态消息,realtime.messages.extension 的值为 presence。您可以在RLS策略中对此进行检查。

允许用户监听频道中的在线状态消息

realtime.messages 表创建 select 策略,条件是 realtime.messages.extension 的值为 presence

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

允许用户在频道中发送在线状态消息

要更新用户的在线状态,需要为 realtime.messages 表创建 insert 策略,条件是 realtime.messages.extension 的值为 presence

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

在线状态与广播

通过在 where 过滤器中包含这两个扩展,可以同时授权在线状态(Presence)和广播(Broadcast)功能。

广播和在线状态读取

通过一条RLS策略同时授权广播和在线状态的读取权限。

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

广播和在线状态写入

通过一条RLS策略同时授权广播和在线状态的写入权限。

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

与Postgres变更的交互

Realtime的Postgres变更功能与频道(Channel)授权是分开的。private频道选项不适用于Postgres变更。

当结合RLS使用Postgres变更时,数据库记录只会发送给根据您的RLS策略允许读取这些记录的客户端。

更新RLS策略

客户端访问策略会在连接期间被缓存。系统不会为每条频道消息都查询数据库。

Realtime会在以下情况下根据您的RLS策略更新客户端的访问策略缓存:

  • 当客户端连接到Realtime并订阅频道时
  • 当客户端通过access_token消息向Realtime发送新的JWT时

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

请确保保持较短的JWT过期时间窗口。