Supabase Flutter匿名UPSERT触发RLS权限错误的解决问询
问题背景
我有一个名为users的Supabase表,包含唯一键文本列id_rc1、文本列version,其余字段均为可空状态。该表已启用行级安全(RLS),配置了匿名用户的INSERT和UPDATE策略(无法复制策略文本,以图片展示)。
使用supabase_flutter的INSERT命令可正常执行,但UPSERT命令始终失败:无论目标id_rc1对应的行是否存在,都会触发错误:"new row violates row-level security policy for table "users", code: 42501, details: Unauthorized, hint: null"。
测试代码
import 'package:flutter/foundation.dart'; import 'package:supabase_flutter/supabase_flutter.dart'; late SupabaseClient supabase; bool supabaseInitialized = false; String supabaseProjectApiUrl = "https://REDACTED.supabase.co"; String supabasePublicAnonKey = "REDACTED"; void main() async { await initSupabase(); supabaseUpsertTest(); } Future<void> initSupabase() async { debugPrint("initSupabase START"); try { await Supabase.initialize(url: supabaseProjectApiUrl, anonKey: supabasePublicAnonKey); supabase = Supabase.instance.client; debugPrint("initSupabase DONE"); supabaseInitialized = true; } catch (err) { debugPrint("initSupabase ERROR: $err"); } } Future<void> supabaseUpsertTest() async { if (supabaseInitialized) { final data = {"id_rc1": "ABC123", "version": "1.0"}; try { debugPrint("Upsert START"); final response = await supabase.from("users").upsert(data, onConflict: "id_rc1"); // final response = await supabase.from("users").insert(data); if (response.error != null) throw response.error; debugPrint("Upsert SUCCESS"); } catch (err) { debugPrint("Upsert ERROR: $err"); } } else { debugPrint("Supabase not initialized"); } }
控制台错误输出
I/flutter (24043): initSupabase START I/flutter (24043): supabase.supabase_flutter: INFO: ***** Supabase init completed ***** I/flutter (24043): initSupabase DONE I/flutter (24043): Upsert START I/flutter (24043): Upsert ERROR: PostgrestException(message: new row violates row-level security policy for table "users", code: 42501, details: Unauthorized, hint: null)
问题
请问需要修改UPDATE策略或Flutter代码中的哪部分,才能让UPSERT正常工作?
解决方案
核心原因
Supabase的UPSERT是INSERT + UPDATE的组合操作:目标行不存在时执行INSERT,存在时执行UPDATE。你的INSERT策略正常,但UPDATE策略的权限限制导致UPDATE分支触发RLS错误。
具体修复步骤
检查并调整UPDATE策略
UPSERT执行UPDATE分支时,会触发UPDATE策略的USING(控制哪些行可被更新)和WITH CHECK(控制更新后的数据是否合规)检查。
若你的UPDATE策略仅允许用户修改关联自身的行(比如基于auth.uid()),匿名用户无用户ID,自然无法匹配。需修改策略允许匿名用户通过id_rc1匹配目标行:-- 示例:允许匿名用户更新指定id_rc1的行,且仅修改version字段 CREATE POLICY "Anonymous update users by id_rc1" ON public.users FOR UPDATE USING (true) -- 或更严格的条件:id_rc1 = current_setting('request.jwt.claims')::json->>'id_rc1'(若前端传递标识) WITH CHECK (version IS NOT NULL); -- 确保更新的字段符合要求验证策略覆盖场景
UPSERT会同时触发INSERT和UPDATE的策略检查(即使其中一个分支不执行),需确保:- INSERT策略允许匿名用户插入包含
id_rc1和version的行 - UPDATE策略允许匿名用户匹配目标
id_rc1的行,且允许修改version字段
- INSERT策略允许匿名用户插入包含
代码无需调整
你的Flutter代码中upsert(data, onConflict: "id_rc1")的参数设置正确,无需修改。
快速测试方法
临时将UPDATE策略设置为全权限(仅用于验证):
ALTER POLICY "你的原有UPDATE策略名称" ON public.users FOR UPDATE USING (true) WITH CHECK (true);
重新运行测试代码,若UPSERT成功,说明问题确实出在原UPDATE策略的条件限制上,再根据业务需求调整策略的严格性即可。
内容的提问来源于stack exchange,提问作者JRose

