如何在Supabase中结合SSR认证与RLS实现文件上传?
问题诊断与解决方案
核心问题分析
从错误日志new row violates row-level security policy (USING expression)可以定位两个关键问题:
- 你的RLS策略仅配置了
WITH CHECK(插入/更新时的校验规则),但缺少对应的USING条件(数据访问的前置校验规则),Supabase存储的objects表RLS需要同时管控读取和写入权限。 - 浏览器客户端未完成有效认证,导致
auth.uid()返回null,无法匹配策略中owner_id = auth.uid()::text的校验条件。
解决方案
1. 完善RLS策略
修改策略,同时添加USING和WITH CHECK条件,覆盖读取、插入、更新全场景:
BEGIN; -- 允许用户读取自己的头像 CREATE POLICY "Allow users to read own avatar" ON "storage"."objects" FOR SELECT USING (bucket_id = 'avatar'::text AND owner_id = auth.uid()::text); -- 允许用户插入/更新自己的头像 CREATE POLICY "Allow users to insert/update own avatar" ON "storage"."objects" FOR INSERT, UPDATE WITH CHECK (bucket_id = 'avatar'::text AND owner_id = auth.uid()::text); COMMIT;
如果是修改已有策略,将CREATE POLICY替换为ALTER POLICY即可。
2. 确保客户端认证会话同步
在SSR场景下,需要将服务器端的认证会话同步到客户端,以保证客户端Supabase实例能获取到用户身份:
服务器端(Next.js 示例)
export async function getServerSideProps(context) { const supabase = createServerClient( process.env.NEXT_PUBLIC_SUPABASE_URL, process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY, { context } ); const { data: { session } } = await supabase.auth.getSession(); return { props: { session } }; }
客户端组件
import { useEffect } from 'react'; import { createBrowserClient } from '@supabase/ssr'; export default function AvatarUploadPage({ session }) { const supabase = createBrowserClient( process.env.NEXT_PUBLIC_SUPABASE_URL, process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY ); // 同步服务器端会话到客户端 useEffect(() => { if (session) { supabase.auth.setSession(session); } }, [session, supabase]); // 上传前先校验用户登录状态 const handleUpload = async () => { const { data: { user }, error: authError } = await supabase.auth.getUser(); if (authError || !user) { console.error('用户未登录或认证失败'); return; } const { error: avatarError } = await supabase.storage.from("avatar").upload( avatar_path, image, { upsert: true } ); if (avatarError) console.error(avatarError); }; return <button onClick={handleUpload}>上传头像</button>; }
3. 显式指定owner_id(可选)
如果Supabase自动填充owner_id失效,可以在上传时显式传入用户ID:
const { data: { user } } = await supabase.auth.getUser(); const { error: avatarError } = await supabase.storage.from("avatar").upload( avatar_path, image, { upsert: true, metadata: { owner_id: user.id } } );
内容的提问来源于stack exchange,提问作者upcoach project
相关产品推荐
相关产品推荐

