Skip to content
Realtime

实时授权

你可以通过在 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:

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

它是怎么运作的 #

🌐 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.

create policy "authenticated can read all messages on topic"
on "realtime"."messages"
for select
to authenticated
using (
(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 select
to authenticated
using (
-- 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_users
for select
to authenticated
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:

create policy "authenticated can receive broadcast"
on "realtime"."messages"
for select
to authenticated
using (
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 insert
to authenticated
with 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 select
to authenticated
using (
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 insert
to authenticated
with 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 select
to authenticated
using (
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 insert
to authenticated
with 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.