实时授权
你可以通过在 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 策略。
🌐 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.
表 realtime.messages 默认启用了行级安全(RLS),你不需要执行 ALTER TABLE realtime.messages ENABLE ROW LEVEL SECURITY。
🌐 Row level security (RLS) is enabled by default on table realtime.messages, you don't need to execute ALTER TABLE realtime.messages ENABLE ROW LEVEL SECURITY.
当用户连接时会进行验证。当他们的 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.
1create policy "authenticated can read all messages on topic"2on "realtime"."messages"3for select4to authenticated5using (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.
1create policy "authenticated with supabase.io email can read all"2on "realtime"."messages"3for select4to authenticated5using (6 -- Only users with the email claim ending with @supabase.io7 (((current_setting('request.jwt.claims'))::json ->> 'email') ~~ '%@supabase.io')8);示例 #
🌐 Examples
以下示例使用这个模式:
🌐 The following examples use this schema:
1create table public.rooms (2 id bigint generated by default as identity primary key,3 topic text not null unique4);56GRANT SELECT ON public.rooms TO anon;78alter table public.rooms enable row level security;910create table public.profiles (11 id uuid not null references auth.users on delete cascade,12 email text NOT NULL,1314 primary key (id)15);1617GRANT SELECT ON public.profiles TO anon;18GRANT SELECT, INSERT, UPDATE, DELETE ON public.profiles TO authenticated;1920alter table public.profiles enable row level security;2122create 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_timestamp26);2728GRANT SELECT ON public.rooms_users TO authenticated;2930alter table public.rooms_users enable row level security;3132create policy "authenticated can read own room memberships"33on public.rooms_users34for select35to authenticated36using ((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:
1create policy "authenticated can receive broadcast"2on "realtime"."messages"3for select4to authenticated5using (6exists (7 select8 user_id9 from10 rooms_users11 where12 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.
1import { createClient } from '@supabase/supabase-js'2const supabase = createClient('your_project_url', 'your_supabase_api_key')34// ---cut---5const channel = supabase.channel('room-1', {6 config: { private: true },7})89channel10 .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:
1create policy "authenticated can send broadcast on topic"2on "realtime"."messages"3for insert4to authenticated5with check (6 exists (7 select8 user_id9 from10 rooms_users11 where12 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.extension是presence。
🌐 Create a policy for select on realtime.messages where realtime.messages.extension is presence.
1create policy "authenticated can listen to presence in topic"2on "realtime"."messages"3for select4to authenticated5using (6 exists (7 select8 user_id9 from10 rooms_users11 where12 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.
1create policy "authenticated can track presence on topic"2on "realtime"."messages"3for insert4to authenticated5with check (6 exists (7 select8 user_id9 from10 rooms_users11 where12 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.
1create policy "authenticated can listen to broadcast and presence on topic"2on "realtime"."messages"3for select4to authenticated5using (6 exists (7 select8 user_id9 from10 rooms_users11 where12 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.
1create policy "authenticated can send broadcast and presence on topic"2on "realtime"."messages"3for insert4to authenticated5with check (6 exists (7 select8 user_id9 from10 rooms_users11 where12 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.