Supabase API Keys解密:Anon Key vs Service Role Key 与RLS陷阱
Supabase API Keys解密:Anon Key vs Service Role Key 与RLS陷阱
夏佳怡引言
Supabase 作为一个强大的开源 Firebase 替代品,提供了便捷的后端服务,包括数据库、认证、存储等。与 Supabase 交互的核心是其 API Keys。当你创建一个 Supabase 项目时,系统会自动生成两个关键的密钥:anon
key 和 service_role
key。理解这两者的区别和正确使用场景至关重要,否则,你可能会像许多开发者(包括我)一样,陷入因行级安全(Row Level Security, RLS)策略而无法获取数据的困境,尤其是在服务端开发中。本文将深入探讨这两个密钥,并结合实际踩坑经验,帮助你避免同样的错误。
API Keys 与 PostgreSQL 角色
首先,需要理解 Supabase 的 API Keys 本质上是映射到 PostgreSQL 数据库角色的长效 JWT(JSON Web Tokens)。如果你查看项目 API 设置中的密钥,会发现 anon
key 和 service_role
key。同时,在数据库的 Roles
部分,你也能找到对应的 anon
和 service_role
用户角色。
这些 JWT 包含了角色信息、签发时间和很长的过期时间(通常约10年)。
1 | { |
这种设计使得 Supabase 的数据 API 能够与 PostgreSQL 强大的行级安全(RLS)机制紧密集成。
anon
Key:客户端的”访客通行证”
anon
key,顾名思义,代表”匿名”(anonymous)用户。它的设计目标是用于 客户端应用程序(如浏览器、移动应用)中,处理那些 不需要用户登录 或 用户未登录时 的请求。
特点与用途
- 低权限:
anon
角色默认拥有的数据库权限非常有限。 - RLS 强制执行: 使用
anon
key 发出的所有数据库请求 严格遵守 你设置的 RLS 策略。你需要明确地为anon
角色创建策略来允许或禁止访问特定表或行。 - 公共访问控制: 你可以通过 RLS 策略来控制公共数据的访问。例如,允许任何人读取
products
表:或者禁止访问:1
2
3
4
5-- 允许 anon 角色读取 profiles 表
create policy "Allow public read access to profiles"
on profiles for select
to anon
using (true);1
2
3
4
5-- 禁止 anon 角色读取 secrets 表 (通常是默认行为,除非有允许策略)
create policy "Disallow public access to secrets"
on secrets for select
to anon
using (false); -- 或者不创建任何允许 select 的策略 - 与 Supabase Auth 集成: 当用户通过 Supabase Auth 登录后,客户端库会自动将请求的角色从
anon
切换到authenticated
。这意味着你可以为已认证用户设置不同的 RLS 策略:1
2
3
4
5-- 允许已认证用户读取自己的 profile
create policy "Allow authenticated users to read their own profile"
on profiles for select
to authenticated
using (auth.uid() = user_id); -- 假设 profiles 表有 user_id 列
关键点: anon
key 是 可以安全地暴露在客户端代码中 的,因为它受到 RLS 的严格限制。
service_role
Key:服务端的”超级管理员”
service_role
key 则完全不同。它映射到 PostgreSQL 中一个预定义的高权限角色,设计用于 服务端环境 或需要执行管理任务的场景。
特点与用途
- 高权限:
service_role
拥有非常高的权限,可以执行各种管理和维护任务。 - 绕过 RLS: 这是最关键的区别!使用
service_role
key 发出的请求 默认会绕过所有的 RLS 策略。这意味着它可以访问数据库中的任何数据,无论 RLS 如何设置。 - 管理任务: 常用于执行需要完整数据访问权限的任务,如数据迁移、后台任务、数据分析、生成报告等。
- 访问 Auth Schema: 如果需要在服务端查询用户信息(如
auth.users
表),通常需要授予service_role
对auth
schema 下相关表的读取权限:1
grant select on table auth.users to service_role;
安全警告: service_role
key 绝对不能暴露在任何客户端代码或用户可访问的地方。它应该被视为最高级别的机密,仅存储在安全的服务端环境中(例如,环境变量)。Supabase 甚至与 GitHub 合作扫描公开仓库,一旦发现暴露的 service_role
key 会自动吊销并通知你。
常见的陷阱:在服务端误用 anon
Key
现在回到很多开发者(包括我自己)遇到的问题:在服务端代码中(例如 Node.js, Python 后端、Serverless Functions)使用了 anon
key 来访问数据库。
当我这样做时,发现无论如何都无法获取到预期的数据,或者只能获取到部分公开数据。调试了很久,检查了服务端逻辑、数据库连接,最终才意识到问题所在:
服务端代码通过 anon
key 发出的请求,仍然受到了数据库 RLS 策略的限制!
因为 anon
key 的设计就是为了强制执行 RLS。如果你的 RLS 策略没有明确允许 anon
角色访问所需的数据(通常服务端需要访问所有数据或特定用户的数据,而这些策略可能只对 authenticated
角色或特定用户 ID 开放),那么请求自然会被拒绝或返回空结果。
解决方案非常简单:
在需要绕过 RLS、拥有完全数据访问权限的服务端环境中,必须使用 service_role
key。
将服务端 Supabase 客户端初始化时使用的 apiKey
从 anon
key 更换为 service_role
key,问题迎刃而解。
1 | // 错误示例:在 Node.js 服务端使用 anon key |
最佳实践
- 客户端: 始终使用
anon
key。依赖 Supabase Auth 和 RLS 来控制数据访问。 - 服务端:
- 如果需要 绕过 RLS 执行管理任务或访问所有数据,请使用
service_role
key。 - 如果服务端代表 特定用户 操作,并且希望 强制执行该用户的 RLS,可以通过特殊方式(如使用用户的 JWT 调用
supabase.auth.setAuth()
, 或在数据库函数中使用set role
)来模拟用户角色,而不是直接使用service_role
key。但这通常更复杂。
- 如果需要 绕过 RLS 执行管理任务或访问所有数据,请使用
- 安全: 像保护数据库密码一样保护你的
service_role
key。将其存储在环境变量或安全的密钥管理服务中,绝不硬编码或提交到版本控制。 - RLS: 即使在服务端使用
service_role
key,也要为你的表启用并配置好 RLS 策略。这是一种纵深防御策略,确保即使anon
key 或用户 token 意外获得某些权限,数据访问仍然受限。
总结
Supabase 的 anon
key 和 service_role
key 是控制 API 访问权限的两个基本工具,它们与 PostgreSQL 的角色和 RLS 紧密相关。
anon
key 用于客户端,权限低,强制执行 RLS,可以公开。service_role
key 用于服务端,权限高,绕过 RLS,必须保密。
如果在服务端发现无法获取数据,请首先检查你是否错误地使用了 anon
key。切换到 service_role
key(并确保其安全存储)通常能解决由 RLS 引起的服务端数据访问问题。正确理解和使用这两个密钥是构建安全、可靠 Supabase 应用的关键一步。
参考资料:
- Supabase Docs: Understanding API Keys