Supabase行级安全(RLS)配置异常求助
RLS规则关联其他表返回空结果的排查与解决
问题描述
现有以下PostgreSQL数据表结构:
create table public.records ( id uuid not null default gen_random_uuid (), created_at timestamp with time zone not null default now(), content text null, patient_id uuid null, provider_id uuid null, constraint records_pkey primary key (id), constraint records_patient_id_fkey foreign key (patient_id) references patient_profiles (id) on update cascade on delete cascade, constraint records_provider_id_fkey foreign key (provider_id) references provider_profiles (id) on update cascade on delete cascade ) tablespace pg_default; create table public.provider_patient ( id uuid not null default gen_random_uuid (), created_at timestamp with time zone not null default now(), provider_id uuid null, patient_id uuid null, constraint provides_care_to_pkey primary key (id), constraint provider_patient_patient_id_fkey foreign key (patient_id) references patient_profiles (id) on update cascade on delete cascade, constraint provider_patient_provider_id_fkey foreign key (provider_id) references provider_profiles (id) on update cascade on delete cascade ) tablespace pg_default; create table public.provider_profiles ( id uuid not null, first_name text null, last_name text null, phone text null, constraint provider_profiles_pkey primary key (id), constraint provider_profiles_id_fkey foreign key (id) references auth.users (id) on delete cascade ) tablespace pg_default; create table public.patient_profiles ( id uuid not null default gen_random_uuid (), created_at timestamp with time zone not null default now(), first_name text null, last_name text null, phone text null, email text null, constraint patients_pkey primary key (id) ) tablespace pg_default;
表关系:医护人员(provider)与患者是多对多关联;记录(records)属于单个患者,一个患者对应多条记录。
需求:配置RLS规则,让医护人员仅能查看自己负责的患者的记录。
测试情况:
- 硬编码UUID的SQL查询可正常返回过滤后的记录:
SELECT * FROM RECORDS where 'cec107e4-90bd-40e8-a233-d0b69a7d4c2c' in ( select provider_id from provider_patient where patient_id = records.patient_id )
- 转换为RLS规则后返回空结果,即使去掉WHERE子句测试仍为空:
( auth.uid () IN ( SELECT provider_patient.provider_id FROM provider_patient WHERE (provider_patient.patient_id = records.patient_id) ) )
使用EXISTS语法编写规则,只要涉及其他表也始终返回空。
问题根源
provider_patient表的RLS或权限未配置:RLS规则执行时,当前用户没有权限访问provider_patient表的行,导致子查询返回空。如果该表未开启RLS,普通用户可能默认没有SELECT权限;如果开启了RLS但未配置对应策略,也会被过滤所有行。- 用户身份不匹配:当前登录用户的
auth.uid()未在provider_profiles中注册为医护人员,或者provider_patient中没有该用户与患者的关联记录。 records表未启用RLS:如果records表本身没有开启行级安全,配置的规则不会生效,但这里返回空更可能是其他表权限问题。
解决方案
1. 配置provider_patient表的访问权限
首先开启provider_patient的RLS:
ALTER TABLE public.provider_patient ENABLE ROW LEVEL SECURITY;
然后创建策略,允许医护人员查看自己的患者关联记录:
CREATE POLICY "Providers can access their patient links" ON public.provider_patient FOR SELECT USING (provider_id = auth.uid());
如果需要支持新增/修改关联记录,可以补充对应的策略。
2. 创建正确的records表RLS规则
使用EXISTS语法编写更高效的查询规则,同时确保规则关联逻辑正确:
-- 先确保records表开启RLS ALTER TABLE public.records ENABLE ROW LEVEL SECURITY; -- 创建查询策略 CREATE POLICY "Providers can view their patients' records" ON public.records FOR SELECT USING ( EXISTS ( SELECT 1 FROM public.provider_patient WHERE provider_patient.provider_id = auth.uid() AND provider_patient.patient_id = records.patient_id ) );
3. 验证关键条件
- 确认当前登录用户的
auth.uid()存在于provider_profiles.id中,即该用户已注册为医护人员。 - 确认
provider_patient表中存在该用户(provider_id)与对应患者(patient_id)的关联记录。 - 直接执行
SELECT * FROM provider_patient作为当前用户,验证是否能返回关联数据,以此确认provider_patient的权限配置正确。
额外排查技巧
如果去掉子查询的WHERE子句仍返回空,说明当前用户无法访问provider_patient的任何行。此时可以:
- 检查
provider_patient表的基础权限:执行GRANT SELECT ON public.provider_patient TO authenticated;(如果用户属于authenticated角色),或者通过RLS策略开放权限。 - 验证
auth.uid()的返回值:执行SELECT auth.uid();确认当前用户的UUID,与provider_patient.provider_id中的值做比对。
内容的提问来源于stack exchange,提问作者VladSofronov
相关产品推荐
相关产品推荐

