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 部分,你也能找到对应的 anonservice_role 用户角色。

这些 JWT 包含了角色信息、签发时间和很长的过期时间(通常约10年)。

1
2
3
4
5
{
"role": "anon",
"iat": 1625137684,
"exp": 1940713684
}

这种设计使得 Supabase 的数据 API 能够与 PostgreSQL 强大的行级安全(RLS)机制紧密集成。

anon Key:客户端的”访客通行证”

anon key,顾名思义,代表”匿名”(anonymous)用户。它的设计目标是用于 客户端应用程序(如浏览器、移动应用)中,处理那些 不需要用户登录用户未登录时 的请求。

特点与用途

  1. 低权限: anon 角色默认拥有的数据库权限非常有限。
  2. RLS 强制执行: 使用 anon key 发出的所有数据库请求 严格遵守 你设置的 RLS 策略。你需要明确地为 anon 角色创建策略来允许或禁止访问特定表或行。
  3. 公共访问控制: 你可以通过 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 的策略
  4. 与 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 中一个预定义的高权限角色,设计用于 服务端环境 或需要执行管理任务的场景。

特点与用途

  1. 高权限: service_role 拥有非常高的权限,可以执行各种管理和维护任务。
  2. 绕过 RLS: 这是最关键的区别!使用 service_role key 发出的请求 默认会绕过所有的 RLS 策略。这意味着它可以访问数据库中的任何数据,无论 RLS 如何设置。
  3. 管理任务: 常用于执行需要完整数据访问权限的任务,如数据迁移、后台任务、数据分析、生成报告等。
  4. 访问 Auth Schema: 如果需要在服务端查询用户信息(如 auth.users 表),通常需要授予 service_roleauth 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 客户端初始化时使用的 apiKeyanon key 更换为 service_role key,问题迎刃而解。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// 错误示例:在 Node.js 服务端使用 anon key
// const supabase = createClient(process.env.SUPABASE_URL, process.env.SUPABASE_ANON_KEY);
// 这会导致请求受 RLS 限制

// 正确示例:在 Node.js 服务端使用 service_role key
import { createClient } from '@supabase/supabase-js'

// 确保 SUPABASE_SERVICE_KEY 存储在安全的环境变量中
const supabaseAdmin = createClient(
process.env.SUPABASE_URL,
process.env.SUPABASE_SERVICE_KEY,
{
auth: {
// 对于 service_role key,最好禁用自动刷新 token
autoRefreshToken: false,
persistSession: false
}
}
)

// 现在 supabaseAdmin 的请求将绕过 RLS
async function getAllUsers() {
const { data, error } = await supabaseAdmin.from('profiles').select('*')
if (error) console.error('Error fetching users:', error)
else console.log('Users:', data)
}

最佳实践

  1. 客户端: 始终使用 anon key。依赖 Supabase Auth 和 RLS 来控制数据访问。
  2. 服务端:
    • 如果需要 绕过 RLS 执行管理任务或访问所有数据,请使用 service_role key。
    • 如果服务端代表 特定用户 操作,并且希望 强制执行该用户的 RLS,可以通过特殊方式(如使用用户的 JWT 调用 supabase.auth.setAuth(), 或在数据库函数中使用 set role)来模拟用户角色,而不是直接使用 service_role key。但这通常更复杂。
  3. 安全: 像保护数据库密码一样保护你的 service_role key。将其存储在环境变量或安全的密钥管理服务中,绝不硬编码或提交到版本控制。
  4. 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 应用的关键一步。


参考资料: