Realtime 授权管理
您可以通过向 realtime.messages
表添加行级安全策略(RLS)来控制客户端对实时广播和在线状态的访问权限。每个RLS策略可以映射到客户端可执行的特定操作:
- 控制哪些客户端可以向频道广播
- 控制哪些客户端可以接收来自频道的广播
- 控制哪些客户端可以发布自己的在线状态到频道
- 控制哪些客户端可以接收其他客户端的在线状态消息
实时授权功能目前处于公开测试阶段。如需在实时频道中使用授权功能,请使用 supabase-js
版本 v2.44.0
或更高版本。
工作原理
Realtime 功能通过数据库 realtime
模式中的 messages
表,在客户端连接到频道主题时为其生成访问策略。
通过在 realtime.messages
表上创建 RLS (行级安全) 策略,您可以控制用户对频道主题及其内部功能的访问权限。
验证过程在用户连接时完成。当 WebSocket 连接建立并加入频道主题时,系统会根据以下因素计算用户权限:
realtime.messages
表上的 RLS 策略- 用户 Auth JWT 中包含的用户信息
- 请求头信息
- 用户尝试连接的频道主题
当 Realtime 为客户端生成策略时,会对 realtime.messages
表执行查询然后回滚。Realtime 不会在您的 realtime.messages
表中存储任何消息。
使用 Realtime 授权功能包含两个步骤:
- 在数据库中为
realtime.messages
创建 RLS 策略 - 在客户端使用
config
选项private: true
实例化 Realtime 频道
注意:RLS 策略复杂度增加可能会影响数据库性能和连接时间,导致连接延迟增加和加入率下降。
辅助函数
在编写 RLS 策略时,您可以使用以下辅助函数:
realtime.topic
返回用户尝试连接的频道主题。
1234567create policy "authenticated can read all messages on topic"on "realtime"."messages"for selectto authenticatedusing ( (select realtime.topic()) = 'room-1');
示例
以下示例使用以下数据库模式:
1234567891011121314151617181920212223create 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
):
12345678910111213141516create 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
来实例化频道。
1234567891011121314151617import { 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
中与请求主题相关联时进行写入(发送)操作:
12345678910111213141516create 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
。
12345678910111213141516create 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
。
12345678910111213141516create 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策略同时授权广播和在线状态的读取权限。
12345678910111213141516create 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策略同时授权广播和在线状态的写入权限。
12345678910111213141516create 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过期时间窗口。