JWT令牌
JSON Web Tokens
JSON Web Token(JWT)是一种数据结构,以字符串形式表示,通常包含用户的身份和授权信息。它编码了令牌的生命周期信息,并使用加密密钥进行签名,使其具备防篡改特性。
Supabase的访问令牌就是JWT。每次向Supabase服务发起请求时都会携带该JWT。通过验证令牌并检查其中包含的声明(claims),您可以允许或拒绝对资源的访问。行级安全策略正是基于JWT中的信息来实现的。
JWT 的编码与签名
JWT(JSON Web Token)的编码和签名过程如下:
JSON 对象最初看起来是这样的:
123456{ "sub": "0001", "name": "Sam Vimes", "iat": 1516239022, "exp": 1518239022}
其中:
sub
表示"subject"(主体),通常是用户的 UUIDname
是用户姓名iat
是令牌创建时的 Unix 时间戳exp
是令牌的过期时间(可选)
以下是一个包含自定义字段的 JWT 示例:
12345678910{ "sub": "0002", "name": "Věra Hrabánková", "iat": 1516239022, "exp": 1518239022, "theme": { "primary" : "#D80C14", "secondary" : "#FFFFFF" }}
注意:令牌中存储的数据越多,编码后的字符串就越长。
编码过程
使用 HS256
等算法对数据进行编码。可以使用 jsonwebtoken
等库进行编码/解码:
12// 示例代码来自 https://replit.com/@awalias/jsonwebtokens#index.jslet token = jwt.sign({ name: 'Sam Vimes' }, 'some-secret')
生成的 JWT 字符串格式如下:
123eyJhbGciOiJIUzI1NiJ9 .eyJzdWIiOiIwMDAxIiwibmFtZSI6IlNhbSBWaW1lcyIsImlhdCI6MTUxNjIzOTAyMiwiZXhwIjoxNTE4MjM5MDIyfQ .zMcHjKlkGhuVsiPIkyAkB2rjXzyzJsMMgpvEGvGtjvA
JWT 结构解析
JWT 由三部分组成:
- 头部
eyJhbGciOiJIUzI1NiJ9
:
123{ "alg": "HS256"}
- 载荷
eyJzdWIiOiIwMDAxIiwibmFtZSI6IlNhbSBWaW1lcyIsImlhdCI6MTUxNjIzOTAyMiwiZXhwIjoxNTE4MjM5MDIyfQ
:
123456{ "sub": "0001", "name": "Sam Vimes", "iat": 1516239022, "exp": 1518239022}
- 签名
zMcHjKlkGhuVsiPIkyAkB2rjXzyzJsMMgpvEGvGtjvA
: 签名是通过以下方式生成的:
12345HMACSHA256( base64UrlEncode(header) + "." + base64UrlEncode(payload) <jwt_secret>)
您可以在 https://jwt.io 上测试生成自己的令牌。
重要说明
- 任何拥有
jwt_secret
的人都可以创建和验证令牌 - 更高级的 JWT 算法使用两个密钥:一个用于创建令牌,另一个用于验证
JWT 的优势
JWT 之所以流行,是因为在微服务架构中:
- 多个独立的微服务需要验证用户身份
- 传统的会话令牌需要中心化验证,效率低下
- JWT 实现了去中心化验证,任何拥有
jwt_secret
的服务都可以独立验证令牌
注意事项
JWT 的一个缺点是难以作废。如果令牌被泄露,恶意攻击者可以在到期前一直使用它,除非系统所有者更新 jwt_secret
(但这会使所有现有令牌失效)。
Supabase 中的 JWT 令牌
在 Supabase 中,我们颁发三种不同用途的 JWT 令牌:
匿名密钥(anon key)
:该密钥用于绕过 Supabase API 网关,可在客户端代码中使用。服务角色密钥(service role key)
:该密钥拥有超级管理员权限,可绕过行级安全策略。切勿将其放入客户端代码中,需保持私密。用户专属 JWT
:这些是我们颁发给登录您项目/服务/网站用户的令牌,相当于现代版的会话令牌,用户可用它来访问专属内容或权限。
第一种 anon key
令牌是开发者在需要与 Supabase 数据库交互时随 API 请求一起发送的。
假设您想读取表 colors
中所有行的名称,可以发起如下请求:
12curl 'https://xscduanzzfseqszwzhcy.supabase.co/rest/v1/colors?select=name' \-H "apikey: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoiYW5vbiIsImlhdCI6MTYxNDIwNTE3NCwiZXhwIjoxOTI5NzgxMTc0fQ.-NBR1WnZyQGpRLdXJfgfpszoZ0EeE6KHatJsDPLIX8c"
将该令牌放入 https://jwt.io 解码后可见:
123456{ "role": "anon", "iss": "supabase", "iat": 1614205174, "exp": 1929781174}
此 JWT 由开发者专属的 jwt_secret
签名(您可以在仪表板的 设置 > API 页面找到该密钥,与编码后的 "anon key" 并列),用于通过 Supabase API 网关访问开发者项目。
该密钥的设计理念是它可以安全地放入客户端,即终端用户看到此密钥也无妨——但前提是您必须首先启用行级安全(Row Level Security)。
第二个密钥 service role key
应仅用于您自己的服务器或环境,切勿与终端用户共享。您可能会使用此令牌执行批量数据插入等操作。
用户访问令牌(user access token)
是在调用以下方法时颁发的 JWT:
1234supabase.auth.signIn({ email: 'valid.email@supabase.io', password: 'They_Live_1988!',})
此令牌需要作为 Authorization Bearer
标头与 apikey
标头一起传递:
123curl 'https://xscduanzzfseqszwzhcy.supabase.co/rest/v1/colors?select=name' \-H "apikey: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoiYW5vbiIsImlhdCI6MTYxNDIwNTE3NCwiZXhwIjoxOTI5NzgxMTc0fQ.-NBR1WnZyQGpRLdXJfgfpszoZ0EeE6KHatJsDPLIX8c" \-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJhdXRoZW50aWNhdGVkIiwiZXhwIjoxNjE1ODI0Mzg4LCJzdWIiOiIwMzM0NzQ0YS1mMmEyLTRhYmEtOGM4YS02ZTc0OGY2MmExNzIiLCJlbWFpbCI6InNvbWVvbmVAZW1haWwuY29tIiwiYXBwX21ldGFkYXRhIjp7InByb3ZpZGVyIjoiZW1haWwifSwidXNlcl9tZXRhZGF0YSI6bnVsbCwicm9sZSI6ImF1dGhlbnRpY2F0ZWQifQ.I-_oSsJamtinGxniPETBf-ezAUwDW2sY9bJIThvdX9s"
您会注意到此令牌明显更长,因为它包含用户专属信息:
1234567891011{ "aud": "authenticated", "exp": 1615824388, "sub": "0334744a-f2a2-4aba-8c8a-6e748f62a172", "email": "valid.email@supabase.io", "app_metadata": { "provider": "email" }, "user_metadata": null, "role": "authenticated"}
若使用服务角色密钥,需将其同时传入 apikey
和 authorization
标头(再次强调,仅限在您自己的服务器等安全环境中使用):
123curl "$YOUR_PROJECT_URL/rest/v1/colors?select=name" \ -H "apikey: $YOUR_SERVICE_ROLE_KEY" \ -H "authorization: Bearer $YOUR_SERVICE_ROLE_KEY"
现在您已了解 JWT 是什么以及它们在 Supabase 中的用途,接下来可以探索如何结合行级安全策略来限制对 Postgres 数据库中特定表、行和列的访问。
资源
- JWT 调试工具: https://jwt.io/