实时授权
你可以通过在 realtime.messages 表上添加行级安全策略来控制客户端对 Realtime Broadcast 和 Presence 的访问。每个 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:
- 控制哪些客户端可以向通道广播
- 控制哪些客户端可以接收通道的广播
- 控制哪些客户端可以向通道发布他们的状态
- 控制哪些客户端可以接收关于其他客户端状态的消息
要强制使用私有通道,你需要在 实时设置 中关闭“允许公开访问”选项
🌐 To enforce private channels you need to disable the 'Allow public access' setting in Realtime Settings
它是怎么运作的 #
🌐 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.
Realtime 会锁定 realtime 模式,以防止意外更改,从而保证 Realtime 服务的正常运行,并避免未来迁移可能引起的冲突。在 realtime 中创建表或函数通常会失败并报错 permission denied for schema realtime,无论你是自己运行 SQL 还是通过仪表盘操作。管理 realtime.messages 的 RLS 策略是被允许的:supautils 允许 postgres 角色在不拥有该表的情况下运行策略语句。
🌐 Realtime locks down the realtime schema to protect it against unexpected changes to guarantee the healthy operation of the Realtime service and avoid conflicts that could be caused by future migrations. Creating a table or function in realtime is expected to fail with permission denied for schema realtime, whether you run the SQL yourself or through the dashboard. Managing RLS policies on realtime.messages is allowed: supautils lets the postgres role run policy statements on that table without owning it.
行级安全已经在 realtime.messages 上启用了,所以不要在迁移中添加 ALTER TABLE realtime.messages ENABLE ROW LEVEL SECURITY。Postgres 会在检查设置是否会改变之前检查表的所有权,因此语句会因为 42501 must be owner of table messages 而失败。这会中止事务,之后的每条语句都会被跳过,包括后面的任何 create policy 语句。
🌐 Row Level Security is already enabled on realtime.messages, so don't add ALTER TABLE realtime.messages ENABLE ROW LEVEL SECURITY to a migration. Postgres checks table ownership before it checks whether the setting would change, so the statement fails with 42501 must be owner of table messages. That aborts the transaction, and every statement after it is skipped, including any create policy statements that follow.
删除 ALTER TABLE 行以及其余的迁移步骤。如果迁移需要检查 RLS 是否开启,请查阅目录:
🌐 Remove the ALTER TABLE line and the rest of the migration passes. If a migration needs to check that RLS is on, read the catalog:
select relrowsecurity from pg_class where oid = 'realtime.messages'::regclass;如果你遇到那个错误,请查看 必须是表 messages 的所有者。
🌐 If you hit that error, see must be owner of table messages.
当用户连接时会进行验证。当他们的 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实例化实时通道
增加的 RLS 复杂性会影响数据库性能和连接时间,导致连接延迟增加和关联速率下降。
🌐 Increased RLS complexity can impact database performance and connection time, leading to higher connection latency and decreased join rates.
正在访问请求信息 #
🌐 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.
create policy "authenticated can read all messages on topic"on "realtime"."messages"for selectto authenticatedusing ( (select realtime.topic()) = 'room-1');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.
create policy "authenticated with supabase.io email can read all"on "realtime"."messages"for selectto authenticatedusing ( -- Only users with the email claim ending with @supabase.io (((current_setting('request.jwt.claims'))::json ->> 'email') ~~ '%@supabase.io'));示例 #
🌐 Examples
以下示例使用这个模式:
🌐 The following examples use this schema:
create table public.rooms ( id bigint generated by default as identity primary key, topic text not null unique);GRANT SELECT ON public.rooms TO anon;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));GRANT SELECT ON public.profiles TO anon;GRANT SELECT, INSERT, UPDATE, DELETE ON public.profiles TO authenticated;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);GRANT SELECT ON public.rooms_users TO authenticated;alter table public.rooms_users enable row level security;create policy "authenticated can read own room memberships"on public.rooms_usersfor selectto authenticatedusing ((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:
create policy "authenticated can receive broadcast"on "realtime"."messages"for selectto authenticatedusing (exists ( select user_id from rooms_users where user_id = (select auth.uid()) and room_topic = (select realtime.topic()) and realtime.messages.extension in ('broadcast') ));然后,要加入一个启用了 RLS 的话题,需要在实例化 Channel 时将 private 选项设置为 true。
🌐 Then, to join a topic with RLS enabled, instantiate the Channel with the private option set to true.
const = .('room-1', { : { : true },}) .('broadcast', { : 'test' }, () => .()) .((, ) => { if ( === 'SUBSCRIBED') { .('Connected!') } else { .() } })允许用户发送广播消息 #
🌐 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:
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 room_topic = (select realtime.topic()) and realtime.messages.extension in ('broadcast') ));存在 #
🌐 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.extension是presence。
🌐 Create a policy for select on realtime.messages where realtime.messages.extension is presence.
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 room_topic = (select realtime.topic()) and realtime.messages.extension in ('presence') ));允许用户在通道上发送在线状态消息 #
🌐 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.
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 room_topic = (select realtime.topic()) and realtime.messages.extension in ('presence') ));出席和广播 #
🌐 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.
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 room_topic = (select realtime.topic()) and realtime.messages.extension in ('broadcast', 'presence') ));广播和在场写 #
🌐 Broadcast and Presence write
在一个 RLS 策略中授权 Presence 和 Broadcast 写入。
🌐 Authorize Presence and Broadcast write in one RLS policy.
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 room_topic = (select realtime.topic()) and realtime.messages.extension in ('broadcast', 'presence') ));与 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
这意味着,如果你在用户仍然连接的时候撤销他们的访问权限(例如,通过删除 RLS 策略用来授予访问的某一行),他们会继续收到消息,直到他们的 JWT 过期或发送新的 JWT 为止。
🌐 This means that if you revoke a user's access (for example, by removing a row an RLS policy uses to grant it) while they're still connected, they'll keep receiving messages until their JWT expires or a new one is sent.
如果在通道上从未收到新的 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.