用户认证(Authentication)
场景介绍
任何面向最终用户的商用应用或服务,都绕不开“谁在用我的应用”这一问题:要区分用户身份、归属数据、控制可见内容、记录操作来源。用户认证(Authentication)正是解决此问题的模块,负责注册、登录、会话维持与注销,为每位用户分配唯一身份并在后续请求中持续验证。如同门禁先识别访客才放行,应用也须先识别用户,才能放行后续操作。
传统做法通常需要客户自行研发,包括建立用户表、编写密码加密与校验逻辑、实现令牌签发与会话刷新功能、找回密码机制以及第三方对接等,同时还需兼顾防撞库、防重放、防注入与安全合规要求,导致研发与运维成本较高。
华为云RDS Supabase的认证模块开箱即用,其核心作用是帮助开发者快速、安全地完成用户注册、登录及权限控制,无需从零构建复杂的安全底座,可显著缩短研发周期;密码哈希、会话安全、防暴力破解等安全机制均由平台托管,符合现代网络安全标准,有效降低自建账号系统漏洞导致的数据泄露风险。
模块提供邮箱密码与GitHub单点登录(SSO)两种方式,覆盖常见登录场景;自动处理访问令牌(Access Token)与刷新令牌(Refresh Token)的生成、分发与刷新,维持安全的登录状态;并与行级安全(RLS)深度整合。登录后自动获得authenticated身份,可按当前登录用户UID在数据库层面限制数据读写,未登录仅持anon低权限(实际行级隔离需配合RLS策略配置,详见数据库API(Database Data API))。模块独立可用,可灵活接入您的应用。本章介绍认证方式、Auth配置及SDK操作。
认证方式
华为云RDS Supabase支持以下两种认证方式,详见表1。
| 认证方式 | 适用场景 |
|---|---|
| 邮箱认证(SMTP) | 用户使用邮箱 + 密码注册与登录,系统通过SMTP发送确认邮件完成验证。适用于需要自主管理用户账号体系、面向通用用户的场景。 |
| GitHub OAuth | 用户使用GitHub账号进行第三方单点登录(SSO),无需在应用内另行注册。适用于面向开发者、技术团队,或希望借助GitHub既有账号体系快速登录的场景。 |
华为云RDS Supabase不支持手机号/SMS、微信、支付宝、Google账号登录;GitHub是唯一支持的第三方OAuth登录方式。
认证状态与RLS角色
认证模块与行级安全(RLS)联动,登录状态决定访问数据时所用的数据库角色,进而决定可见数据范围。
| 认证状态 | RLS角色 | 说明 |
|---|---|---|
| 未登录 + Publishable Key | anon | 匿名用户,仅受anon策略约束。 |
| 已登录 + Publishable Key | authenticated | 认证用户,受authenticated策略约束。 |
| Secret Key / Service Role Key | Bypass RLS | 服务端高权限,绕过行级安全。 |
实际行级隔离需配合RLS策略配置,详见数据库API(Database Data API)。
Auth配置
Auth配置是控制台集中管理认证能力的入口,可在此完成认证方式的启停与参数调配:启用并配置邮箱认证、GitHub第三方登录,控制用户注册与匿名访问,设置各类邮件模板等。
Auth配置入口:单击Supabase应用名称,进入应用详情页,即可看到Auth配置入口,详细操作见用户认证Auth配置管理。
| 配置类别 | 说明 |
|---|---|
| 邮箱IDP(SMTP) | SMTP主机、端口、用户名、密码(授权码)、管理员邮箱、发件人姓名、邮箱自动确认、发件速率限制等,详见配置IDP身份验证提供商(邮箱)。 |
| GitHub IDP | GitHub OAuth App的Client ID、Client Secret、回调URL,详见配置IDP身份验证提供商(GitHub)。 |
| 访问控制 | 站点URL、允许重定向的URL列表、是否允许新用户注册、是否允许匿名用户,详见访问控制。 |
| 邮件模板 | 涵盖邮箱确认、邀请、密码重置、邮箱变更、魔法链接、重新认证等场景,模板可用Storage Bucket托管,详见配置邮箱模板。 |
邮件模板中的魔法链接对应无密码邮箱登录(通过signInWithOtp发送一次性登录链接)。华为云RDS Supabase当前主推邮箱密码认证,魔法链接模板为预置项,供按需扩展使用,本文不展开介绍。
邮箱认证(SMTP)
SMTP(简单邮件传输协议)是发送邮件的标准协议,邮箱认证即依赖SMTP服务器发送确认与通知邮件。通过邮箱认证形成完整闭环:注册 → 登录 → 认证状态 → 更新用户(邮箱与密码) → 注销
邮箱认证围绕邮箱+密码与邮件验证构建,支持的核心业务场景与功能如下:
- 注册与登录:用户以邮箱+密码注册、登录。
- 邮箱确认:注册后发送确认邮件验证邮箱归属,防止伪造账号。
- 魔法链接登录:无密码场景,发送一次性登录链接,点击即登录。
- 密码重置:忘记密码时,通过邮箱重置链接恢复访问。
- 邮箱变更:换绑邮箱时,向新邮箱发确认链接,确认后生效。
- 信息与会话维护:更新邮箱/密码、监听登录状态、注销。
各流程邮件均经SMTP发送,模板可在控制台Auth配置中定制。
- 邮箱注册
const { data, error } = await supabase.auth.signUp({ email: 'user@example.com', // 邮箱地址 password: 'your-password', // 邮箱密码 })调用后系统创建用户并发送确认邮件。若控制台开启了“邮箱自动确认”,邮箱即时验证通过,用户注册后即处于登录态;否则需点击确认邮件中的链接完成验证,方可登录获取会话。
前提: 未开启“邮箱自动确认”时,注册依赖已配置且可达的SMTP服务发送确认邮件;若SMTP未配置或不可达,用户将无法收到确认邮件、无法完成注册。
- 邮箱登录
const { data, error } = await supabase.auth.signInWithPassword({ email: 'user@example.com', // 邮箱地址 password: 'your-password', // 邮箱密码 })调用返回{ data, error }:成功时error为null、data含登录会话;失败时(如密码错误、用户不存在)error非空,根据该错误,判断并提示用户。
if (error) { console.error('登录失败:', error.message) return } - 监听认证状态
会话(session)是用户登录后的凭证,登录凭证(JWT)具备有效期;SDK会在凭证过期前自动续期、无需手动处理,TOKEN_REFRESHED事件在凭证续期时触发。
可监听认证状态变化以联动界面。
supabase.auth.onAuthStateChange((event, session) => { console.log('认证事件:', event) console.log('当前会话:', session) }) - 邮箱变更
const { data, error } = await supabase.auth.updateUser({ email: 'new-email@example.com' }) if (error) { console.error('邮箱变更失败:', error.message) return }用户必须处于登录状态才能调用updateUser()。调用后系统向新邮箱发送“邮箱变更”确认邮件,用户须点击确认链接后新邮箱才正式生效;未确认前旧邮箱仍可用于登录。
- 密码重置 用户忘记密码时,可通过邮箱重置:先发送重置邮件,用户点击邮件中的重置链接回到应用后,再设置新密码。
// 发送密码重置邮件 const { data, error } = await supabase.auth.resetPasswordForEmail( 'user@example.com', // 邮箱地址 { emailRedirectTo: 'https://your-app.com/reset-password' } ) if (error) { console.error('发送重置邮件失败:', error.message) return }用户点击重置链接后,被重定向回应用并触发PASSWORD_RECOVERY事件,此时处于密码重置状态,可在重置页面调用updateUser设置新密码。
// 用户输入新密码后重置 const { data, error } = await supabase.auth.updateUser({ password: 'new-password' }) if (error) { console.error('重置密码失败:', error.message) return }重置链接一次性有效;需在控制台Auth配置中启用邮箱认证并使用“密码恢复”邮件模板。密码可能因强度不足等被拒绝,请判断error并提示用户。
- 用户注销
// 默认登出当前用户在所有设备的会话 const { error } = await supabase.auth.signOut() // 如仅登出当前会话(保留其他设备登录),显式指定 local const { error } = await supabase.auth.signOut({ scope: 'local' })signOut()默认scope为global,会登出该用户在所有设备的会话;若只想退出当前会话,须显式传{ scope: 'local' }。两种方式均会触发SIGNED_OUT事件。
- 魔法链接(无密码登录) 魔法链接(Magic Link)是一种无密码邮箱登录方式:用户输入邮箱,系统发送一封含一次性登录链接的邮件,用户点击链接即完成登录,无需记忆密码。适用于希望降低注册门槛、减少密码运维的场景。
// 发送魔法链接邮件 const { data, error } = await supabase.auth.signInWithOtp({ email: 'user@example.com', // 邮箱地址 options: { emailRedirectTo: 'https://your-app.com/dashboard' } }) if (error) { console.error('发送登录链接失败:', error.message) return }用户收到邮件后点击链接,即被重定向回应用并完成登录,随后触发SIGNED_IN事件;魔法链接一次性有效,登录后即失效。魔法链接依赖已配置且可达的SMTP服务,并使用控制台Auth配置中的“魔法链接”邮件模板。
GitHub OAuth(SSO单点登录)
OAuth是一种开放授权标准,允许用户使用已有的第三方账号登录应用,而无需在应用内另行设置密码。GitHub OAuth即通过GitHub账号实现第三方登录(常被类比为单点登录):用户无需在应用内另行注册账号或设置密码,只需使用已有的GitHub账号授权一次即可登录应用,以GitHub作为可信身份来源。通过GitHub OAuth形成完整闭环:登录 → 认证状态 → 更新用户(邮箱与密码) → 注销
- GitHub登录 调用后,SDK在浏览器端自动重定向至GitHub完成身份认证与授权;用户在GitHub确认后,GitHub回调携带授权码,应用据此换取会话,用户即处于登录态。整个登录过程由GitHub侧保管凭证,应用不接触用户密码。
// 基本调用 const { data, error } = await supabase.auth.signInWithOAuth({ provider: 'github' }) // 指定回调后重定向地址 const { data, error } = await supabase.auth.signInWithOAuth({ provider: 'github', options: { redirectTo: 'https://your-app.com/dashboard' } })回调地址分两层:
- GitHub OAuth App的Authorization callback URL:GitHub完成认证后携带授权码回传至Supabase的地址。
- redirectTo:Supabase处理完授权码后跳转回应用的业务页面地址(如上方示例)。
- 监听认证状态
supabase.auth.onAuthStateChange((event, session) => { console.log('认证事件:', event) console.log('当前会话:', session) })监听方式与事件清单与邮箱认证完全一致,详见表4。
- 邮箱变更
const { data, error } = await supabase.auth.updateUser({ email: 'new-email@example.com' }) if (error) { console.error('邮箱变更失败:', error.message) return }用户必须处于登录状态才能调用updateUser()。调用后系统向新邮箱发送“邮箱变更”确认邮件,用户须点击确认链接后新邮箱才正式生效。用法与邮箱认证一致。
- 密码重置
// 发送密码重置邮件 const { data, error } = await supabase.auth.resetPasswordForEmail( 'user@example.com', // 邮箱地址 { emailRedirectTo: 'https://your-app.com/reset-password' } ) if (error) { console.error('发送重置邮件失败:', error.message) return }用户点击重置链接后触发PASSWORD_RECOVERY事件,在重置页面调用updateUser设置新密码。通过GitHub SSO登录的用户在应用内没有密码,密码重置项可用于为其设置应用内密码;若始终仅用GitHub登录,可跳过密码重置。
- 用户注销
const { error } = await supabase.auth.signOut()signOut()默认scope为global,会登出该用户在所有设备的会话;若只想退出当前会话,须显式传{ scope: 'local' }。