You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Supabase中结合SSR认证与RLS实现文件上传?

问题诊断与解决方案

核心问题分析

从错误日志new row violates row-level security policy (USING expression)可以定位两个关键问题:

  1. 你的RLS策略仅配置了WITH CHECK(插入/更新时的校验规则),但缺少对应的USING条件(数据访问的前置校验规则),Supabase存储的objects表RLS需要同时管控读取和写入权限。
  2. 浏览器客户端未完成有效认证,导致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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.22 06:55:59