Supabase Svelte 用户管理示例全解从零搭建 Svelte 应用与 Postgres 行级安全鉴权【免费下载链接】supabaseThe Postgres development platform. Supabase gives you a dedicated Postgres database to build your web, mobile, and AI applications.项目地址: https://gitcode.com/GitHub_Trending/supa/supabase本指南以 Supabase 官方示例项目 svelte-user-management 为蓝本完整拆解如何在一个基于 Svelte 5 Vite 的前端应用中集成 Supabase Auth、实时会话监听、Profiles 数据表 CRUD 与 Storage 头像上传。读完本文你将掌握从「创建项目、执行 Quickstart SQL、配置环境变量」到「Magic Link 登录、按用户写入个人资料、受 RLS 保护的头像存取」的端到端实战方案并能在仓库源码层面理解每一行关键实现。示例项目概览examples/user-management/svelte-user-management 是 Supabase 仓库中 user-management 系列示例的 Svelte 实现。它演示了一条完整的用户管理链路用户通过邮箱 Magic Link 登录 → 应用根据登录会话展示「登录」或「账户资料编辑」两种界面 → 用户可维护username、website、avatar_url并上传头像到 Storage。项目的技术栈以 package.json 为准依赖版本区间用途svelte^5.37.3UI 框架使用 Svelte 5 的 runes 语法supabase/supabase-js^2官方 JS 客户端负责 Auth、数据库与 Storagevite^7.0.6开发服务器与构建工具sveltejs/vite-plugin-svelte^6.1.0Svelte 的 Vite 插件typescript/svelte-check~5.9.2/^4.3.1类型检查整个示例没有引入 SvelteKit而是以纯 Vite Svelte 的 SPA 形式组织更聚焦于 Supabase 客户端 API 本身便于单独抽出复用。安装依赖与可用脚本进入示例目录后先安装依赖npm install安装完成后项目目录下可用的脚本对应 package.json命令作用npm run dev以开发模式启动应用浏览器打开http://localhost:5173即可访问修改源码后页面会热更新重载npm run build构建生产版本产物输出到dist文件夹。Vite 会在生产模式下正确打包 Svelte 并优化构建产物文件名带内容哈希npm run preview本地预览生产构建产物npm run check执行svelte-check与tsc做类型与编译期检查Vite 的开发服务器默认监听 5173 端口vite.config.ts 仅注册了svelte()插件保持最小化配置。从零搭建五步快速上手1. 创建 Supabase 项目前往 Supabase Dashboard 注册并创建新项目等待数据库启动完成。每个 Supabase 项目都是一个完整的 Postgres 数据库实例。2. 运行 User Management Quickstart SQL数据库启动后进入项目的 SQL Editor运行User Management Starter快速启动脚本在 SQL editor 页面滚动找到 “User Management Starter: Sets up a public Profiles table which you can access with your API”点击RUN执行。执行完毕后前往 Table Editor即可看到新建的profiles表。该脚本的关键内容包括三部分建表profiles并开启 RLS——每条用户记录与auth.users通过外键id关联为supabase_realtime发布订阅添加profiles表——让该表支持 Realtime 实时推送创建avatarsStorage bucket 并配置公开读取/任意上传策略。3. 获取 API URL 与密钥在 Project Settings齿轮图标→ API 页签中找到Project URL / API URL形如https://project-ref.supabase.co的项目接口地址anon/ publishable key客户端 API 密钥。它允许用户在登录前对数据库进行「匿名访问」一旦用户完成登录密钥会切换为该用户自己的登录令牌从而让 Postgres 行级安全RLS生效详见下文「Postgres 行级安全」小节。安全警示secretservice_role密钥拥有绕过一切安全策略的完整数据访问权限必须严格保密只允许在服务端环境中使用绝不能放进客户端或浏览器代码。示例中的.env.example与supabaseClient.ts只引用客户端密钥。4. 配置环境变量从.env.example复制生成.env.local并把上面的 URL 与密钥填入# .env.example 的内容见 examples/user-management/svelte-user-management/.env.example VITE_SUPABASE_URLhttps://your-project-ref.supabase.co VITE_SUPABASE_PUBLISHABLE_KEYyour-publishable-key.env.local是本地开发私有文件通常应加入.gitignore避免泄露密钥。Vite 会以import.meta.env的形式向客户端注入以VITE_前缀开头的环境变量。5. 运行应用执行npm run dev并在浏览器打开http://localhost:5173/即进入可交互的登录界面。客户端初始化与运行环境读取src/supabaseClient.ts 是连接 Supabase 的唯一入口import { createClient } from supabase/supabase-js const supabaseUrl import.meta.env.VITE_SUPABASE_URL const supabasePublishableKey import.meta.env.VITE_SUPABASE_PUBLISHABLE_KEY export const supabase createClient(supabaseUrl, supabasePublishableKey)通过 Vite 的类型化环境变量import.meta.env.VITE_*读取配置类型声明位于 src/vite-env.d.tscreateClient(url, key)返回的supabase单例被各个组件共享无需在组件内重复创建。命名提示SDK 新版本将此类客户端密钥统称为 publishable key与旧文档中的anonkey 指代同一角色公开、仅能触发 RLS 校验。若在旧版文档中看到VITE_SUPABASE_ANON_KEY之类的命名其职责等价。会话状态管理与条件渲染src/App.svelte 负责「是否已登录」的全局判断是整条鉴权流程的入口script langts import { onMount } from svelte import { supabase } from ./supabaseClient import type { AuthSession } from supabase/supabase-js import Account from ./lib/Account.svelte import Auth from ./lib/Auth.svelte let session $stateAuthSession | null(null) onMount(() { supabase.auth.getSession().then(({ data }) { session data.session }) supabase.auth.onAuthStateChange((_event, _session) { session _session }) }) /script div classcontainer stylepadding: 50px 0 100px 0 {#if !session} Auth / {:else} Account {session} / {/if} /div可以对照官方用户管理模板的典型模式理解其内部机制首次加载恢复会话onMount中调用supabase.auth.getSession()读取本地持久化的登录态把session从null更新为真实会话或保持null订阅会话变化supabase.auth.onAuthStateChange注册监听器登录、登出、令牌刷新等事件都会触发回调并把最新_session写入响应式状态保证 UI 与真实登录态始终一致Svelte 5 runeslet session $state(...)声明响应式变量配合{#if}实现「未登录渲染Auth /、已登录渲染Account {session} /」的条件分支同时把当前会话对象作为 prop 传给子组件。应用入口 src/main.ts 使用 Svelte 5 新的mountAPI 把App挂载到#app节点并导入全局样式app.css。邮箱 Magic Link 无密码登录src/lib/Auth.svelte 实现免密码登录用户只需输入邮箱点击发送 Magic Link然后在邮箱中点击一次性登录链接即可完成登录。script langts import { supabase } from ../supabaseClient; let loading $state(false); let email $state(); const handleLogin async () { try { loading true; const { error } await supabase.auth.signInWithOtp({ email }); if (error) throw error; alert(Check your email for login link!); } catch (error) { if (error instanceof Error) { alert(error.message); } } finally { loading false; } }; /script form classform-widget onsubmit{(e) { e.preventDefault(); handleLogin(); }} input idemail typeemail placeholderYour email bind:value{email} / button typesubmit disabled{loading} {loading ? Loading : Send magic link} /button /form核心调用是supabase.auth.signInWithOtp({ email })发送请求期间通过loading状态禁用按钮并展示 “Loading”用finally确保无论成败都会复位避免重复提交错误统一经alert弹出提示表单通过onsubmitpreventDefault()拦截默认提交行为转由handleLogin处理Magic Link 的启用/关闭取决于 Supabase 项目 Auth 配置中是否开启邮件 OTP本示例默认走这一路径。账户资料读取与 upsert 更新src/lib/Account.svelte 接收从App.svelte传入的session负责展示并编辑当前用户的资料。读取当前用户资料getProfileconst getProfile async () { loading true; const { user } session; const { data, error, status } await supabase .from(profiles) .select(username, website, avatar_url) .eq(id, user.id) .single(); if (error status ! 406) throw error; if (data) { username data.username; website data.website; avatarUrl data.avatar_url; } };从session.user取出登录用户的id该 UUID 与auth.users一致以.eq(id, user.id)过滤出当前用户的行.single()期望恰好返回一行特殊处理 406当该用户还没有资料行时查询返回406 Not Acceptable此时不视为错误抛出仅保持表单为空等待用户新建资料。保存资料updateProfileconst updates { id: user.id, username, website, avatar_url: avatarUrl, updated_at: new Date().toISOString(), }; const { error } await supabase.from(profiles).upsert(updates);使用upsert存在则更新、不存在则插入把表单内容写回profiles表并手动刷新updated_at时间戳。这种「upsert 主键即 user.id」的写法既避免了「先查后写」的竞态也正好利用了profiles.id引用auth.users.id的主键约束。页面同时提供Sign Out按钮点击即调用supabase.auth.signOut()触发onAuthStateChange回调后App.svelte的session置空界面自动切回登录视图。头像上传Storage bucket 与 Blob 预览src/lib/Avatar.svelte 封装了一个可复用的头像组件对外暴露urlbindable与onupload回调账户页在头像更新完成后会触发updateProfile一并把新的avatar_url落库。下载并预览downloadImageconst { data, error } await supabase.storage .from(avatars) .download(path); const url URL.createObjectURL(data); avatarUrl url;从avatarsbucket 按对象路径下载文件用URL.createObjectURL生成本地 Blob URL 直接预览无需对象公开 URL$effect(() { if (url) downloadImage(url); })是 Svelte 5 的响应式副作用每当传入的url变化如新头像上传成功就重新拉取图片。上传文件uploadAvatarconst file files[0]; const fileExt file.name.split(.).pop(); const filePath ${Math.random()}.${fileExt}; const { error } await supabase.storage .from(avatars) .upload(filePath, file); url filePath; onupload?.();取用户选择的第一个文件用Math.random() 原扩展名生成随机对象路径避免同名文件互相覆盖与缓存冲突生产项目更推荐 UUID、时间戳等方案上传成功后把url回写给父组件$bindable并调用onupload?.()通知保存资料模板用隐藏的原生input typefile acceptimage/*触发文件选择样式上以按钮呈现。整个链路可总结为上传头像到avatarsbucket → 把对象路径存进profiles.avatar_url→ 下次进入时按路径download并createObjectURL展示。Postgres 行级安全RLS原理该示例的高层授权完全建立在 Postgres 行级安全Row Level Security之上这是整个方案安全性的根基Supabase 中的每个 Postgres 数据库都预置了authschema 及若干辅助函数用户登录后会获得一个携带角色authenticated与用户 UUID 的 JWTSupabase 据此对「每个用户能做什么、不能做什么」进行细粒度控制数据库层面的 RLS 策略会基于 JWT 中的auth.uid()校验每一行数据的读写权限而不是依赖客户端「隐藏界面元素」这类不可信的防御手段。User Management Starter 生成的表结构与策略以下是该示例所依赖的精简版 schema 及全部策略与官方 Quickstart SQL 一致-- 创建公开资料表Public Profiles create table profiles ( id uuid references auth.users not null, updated_at timestamp with time zone, username text unique, avatar_url text, website text, primary key (id), unique (username), constraint username_length check (char_length(username) 3) ); alter table profiles enable row level security; -- 所有人可查看公开资料read 策略 create policy Public profiles are viewable by everyone. on profiles for select using (true); -- 用户只能插入自己的资料insert 策略 create policy Users can insert their own profile. on profiles for insert with check ((select auth.uid()) id); -- 用户只能更新自己的资料update 策略 create policy Users can update own profile. on profiles for update using ((select auth.uid()) id); -- 启用 Realtime将 profiles 表加入 supabase_realtime 发布订阅 begin; drop publication if exists supabase_realtime; create publication supabase_realtime; commit; alter publication supabase_realtime add table profiles; -- 创建 avatars Storage bucket insert into storage.buckets (id, name) values (avatars, avatars); -- Storage 访问控制允许以公开方式下载头像对象 create policy Avatar images are publicly accessible. on storage.objects for select using (bucket_id avatars and storage.allow_any_operation(array[object.get_authenticated_info, object.get_authenticated])); -- 允许任意人上传头像 create policy Anyone can upload an avatar. on storage.objects for insert with check (bucket_id avatars);逐个策略解读对象策略含义profiles表enable row level security开启 RLS此后所有普通客户端访问都必须经过策略裁决profilesSELECTusing (true)公开资料对所有人可读含未登录的anon角色这是公开展示用户名的前提profilesINSERTwith check (auth.uid() id)只允许用户插入id等于自己 UUID 的行防止越权创建他人资料profilesUPDATEusing (auth.uid() id)只允许用户更新自己的行未提供 DELETE 策略即默认禁止删除profiles表加入supabase_realtime发布开启该表的 Realtime 变更推送能力storage.buckets插入avatars创建公开的avatarsbucketstorage.objectsSELECTbucket_id avatars允许公开下载 avatars bucket 中的对象storage.objectsINSERTbucket_id avatars允许向 avatars bucket 上传示例保持开放以便快速体验生产建议收紧为auth.uid() (storage.foldername(name))[1]::uuid之类按用户隔离的策略理解这张表也就理解了这个示例为何「安全不用在前端做任何权限判断」即便恶意用户直接构造 API 请求也会在数据库层被 RLS 拒绝。账户页对status ! 406的错误统一抛出、Storage 随机对象路径等细节都是对这套权限模型的补充。从源码到文档示例目录结构速览examples/user-management/svelte-user-management/ ├── .env.example # 环境变量模板VITE_SUPABASE_URL / VITE_SUPABASE_PUBLISHABLE_KEY ├── index.html ├── package.json # 脚本与依赖声明 ├── svelte.config.js ├── tsconfig*.json # 应用与 Node 侧分离的 TS 配置 ├── vite.config.ts # 仅注册 svelte() 插件 └── src/ ├── main.ts # mount 入口 ├── App.svelte # 会话管理 登录/资料条件渲染 ├── supabaseClient.ts # createClient 单例 ├── app.css # 全局样式 └── lib/ ├── Auth.svelte # Magic Link 邮箱登录 ├── Account.svelte # 资料读取与 upsert 更新、登出 └── Avatar.svelte # Storage 头像上传与 Blob 预览同系列的其他语言实现如 nextjs-user-management、expo-user-management 等均复用同一份 RLS schema 与用户管理交互模式可互为参照。进阶方向在把本示例用于真实产品前可以继续深化收紧上传策略让用户只能把对象上传到以自己auth.uid()命名的目录avatars/uid/...并把storage.objects的 SELECT/INSERT 策略改为按目录校验补充资料删除与账户注销为profiles增加 DELETE 策略或通过 Database Trigger 在用户删除时级联清理资料与头像接入 Email 之外的登录方式signInWithOtp之外supabase.auth还支持 OAuthsignInWithOAuth、密码signInWithPassword等方式使用 Supabase CLI 管理迁移把上述 Quickstart SQL 落到supabase/migrations目录实现 schema 的版本化——仓库中其他 user-management 示例如 nextjs、flutter均以 migrations 的形式维护这份初始化 SQL可对照参考。整体来看本示例的精髓在于「薄客户端 厚数据库」前端只负责调用 Auth、读写profiles、存取 Storage而数据安全完全交由 Postgres RLS 在服务端裁决。把这份 schema 与组件模式迁移到自己的 Supabase Svelte 项目即可快速获得一套安全、可扩展的用户体系。【免费下载链接】supabaseThe Postgres development platform. Supabase gives you a dedicated Postgres database to build your web, mobile, and AI applications.项目地址: https://gitcode.com/GitHub_Trending/supa/supabase创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考