PostgREST 教程:实现基于JWT的API认证机制
前言
PostgREST 是一个将 PostgreSQL 数据库直接转换为 RESTful API 的轻量级工具。在前一个教程中,我们创建了一个只读的API端点来获取待办事项列表。本教程将深入探讨如何为API添加认证功能,使特定用户能够修改数据。
认证机制概述
PostgREST 使用 JSON Web Tokens (JWT) 进行认证。JWT 是一种开放标准(RFC 7519),用于在各方之间安全地传输信息作为JSON对象。这种认证方式具有以下特点:
- 服务器端使用密钥对令牌进行签名
- 客户端无法篡改令牌内容
- 令牌可以包含过期时间等元数据
- 支持自定义声明(claims)
实现步骤详解
第一步:创建可信用户角色
在PostgreSQL中,我们需要为认证用户创建一个专用角色:
-- 创建不可登录的角色
CREATE ROLE todo_user nologin;
-- 授权给authenticator角色(PostgREST使用的角色)
GRANT todo_user TO authenticator;
-- 授予模式访问权限
GRANT USAGE ON SCHEMA api TO todo_user;
-- 授予表操作权限
GRANT ALL ON api.todos TO todo_user;
这里我们创建了todo_user
角色,并授予它对api.todos
表的完全控制权限。nologin
属性表示这个角色不能直接登录数据库,只能通过PostgREST使用。
第二步:生成JWT密钥
JWT的安全性依赖于密钥的保密性。密钥必须满足:
- 至少32个字符长度
- 使用高熵值随机生成
- 妥善保管,不泄露给客户端
可以使用以下命令生成强密钥:
# 确保处理非UTF-8字节序列
export LC_CTYPE=C
# 生成32字符的字母数字密钥
echo "jwt-secret = \"$(< /dev/urandom tr -dc A-Za-z0-9 | head -c32)\"" >> tutorial.conf
将生成的密钥添加到PostgREST的配置文件中后,需要重启服务使配置生效。
第三步:生成JWT令牌
JWT由三部分组成,用点号(.)分隔:
- Header - 包含算法和类型信息
- Payload - 包含声明(claims)
- Signature - 前两部分的签名
生成令牌的bash脚本示例:
#!/bin/bash
set -e
JWT_SECRET='your_secure_secret_here'
_base64() { openssl base64 -e -A | tr '+/' '-_' | tr -d '='; }
header=$(echo -n '{"alg":"HS256","typ":"JWT"}' | _base64)
payload=$(echo -n "{\"role\":\"todo_user\"}" | _base64)
signature=$(echo -n "$header.$payload" | openssl dgst -sha256 -hmac "$JWT_SECRET" -binary | _base64)
echo -n "$header.$payload.$signature"
重要提示:虽然JWT看起来是加密的,但实际上只是进行了签名。任何人都可以解码并查看payload内容,因此不应在其中存储敏感信息。
第四步:使用令牌进行API操作
获得令牌后,可以通过Authorization头进行认证请求:
# 设置环境变量
export TOKEN="your_generated_token"
# 创建新待办事项
curl http://localhost:3000/todos -X POST \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"task": "学习认证机制"}'
# 批量更新所有待办事项为已完成
curl http://localhost:3000/todos -X PATCH \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"done": true}'
第五步:添加令牌过期时间
为增强安全性,应为JWT设置合理的过期时间。PostgREST支持标准的exp
声明,表示令牌的过期时间(Unix时间戳)。
计算5分钟后的Unix时间戳:
exp=$(( $(date +%s) + 300 ))
echo $exp
然后在生成令牌时包含exp声明:
payload=$(echo -n "{\"role\":\"todo_user\",\"exp\":$exp}" | _base64)
过期后,API将返回401 Unauthorized错误。
高级主题:即时令牌撤销
在某些情况下,即使令牌未过期,也需要立即撤销其访问权限。PostgREST支持通过db-pre-request
配置项实现这一功能。
- 创建验证函数:
CREATE SCHEMA auth;
GRANT USAGE ON SCHEMA auth TO web_anon, todo_user;
CREATE OR REPLACE FUNCTION auth.check_token() RETURNS void
LANGUAGE plpgsql AS $$
BEGIN
IF current_setting('request.jwt.claims', true)::json->>'email' =
'disgruntled@mycompany.com' THEN
RAISE insufficient_privilege
USING hint = '访问已被拒绝';
END IF;
END
$$;
- 更新配置文件:
db-pre-request = "auth.check_token"
- 重启PostgREST后,特定令牌将被拒绝访问。
最佳实践建议
- 生产环境应使用更安全的密钥生成方式
- 设置合理的令牌过期时间(通常几分钟到几小时)
- 考虑实现令牌刷新机制
- 敏感操作应使用更细粒度的权限控制
- 定期轮换JWT密钥
通过本教程,您已经掌握了PostgREST的基本认证机制实现方法。这种基于JWT的认证方式既安全又灵活,能够满足大多数API的认证需求。
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考