欢迎使用PostgREST!在本教程中,我们将使事情开始运行,以便您可以创建第一个简单的API。
PostgREST是一个独立的Web服务器,它将PostgreSQL数据库转换为RESTful API。它提供基于基础数据库的结构自定义的API。
为了制作一个API,我们将简单地建立一个数据库。所有端点和权限都来自数据库对象,例如表,视图,角色和存储过程。这些教程将涵盖许多常见方案以及如何在数据库中对其进行建模。
在本教程结束时,您将拥有一个正常运行的数据库,PostgREST服务器和一个简单的单用户待办事项列表API。
在开始本教程时,在另一个选项卡中弹出以打开项目聊天室。有很多人参与该项目,如果您遇到困难,我们会帮助您。
您将需要在系统上本地运行或在Docker实例中运行数据库的现代副本。我们要求PostgreSQL 9.3或更高版本,但建议至少使用9.5,以便在以后的教程中使用行级安全性功能。
如果您已经熟悉使用PostgreSQL并将其安装在系统上,则可以使用现有安装。在本教程中,我们将描述如何在Docker中使用数据库,因为对于一个简单的教程而言,数据库配置太复杂了。
如果未安装Docker,则可在此处获取。接下来,让我们拉起并启动数据库映像:
sudo docker run --name tutorial -p 5433:5432 \
-e POSTGRES_PASSWORD=mysecretpassword \
-d postgres
这会将Docker实例作为守护程序运行,并将端口5433暴露给主机系统,以便在系统的其余部分看起来像普通的PostgreSQL服务器。
PostgREST以单个二进制文件的形式分发,其版本针对Linux / BSD / Windows的主要发行版进行了编译。访问最新版本以获取下载列表。如果您的平台不在预先构建的平台中,请参阅从源代码构建以获取有关如何自行构建平台的说明。还请告知我们在下一个版本中添加您的平台。
用于下载的预构建二进制文件是.tar.xz
压缩文件(Windows是zip文件除外)。要提取二进制文件,请进入终端并运行
# download from https://github.com/PostgREST/postgrest/releases/latest
tar xfJ postgrest--.tar.xz
结果将是一个简单命名的文件postgrest
(或postgrest.exe
在Windows上)。此时,请尝试使用
./postgrest
如果一切正常,它将打印出其版本和有关配置的信息。您可以从下载位置继续运行此二进制文件,也可以将其复制到/usr/local/bin
Linux上的系统目录中,以便可以从任何目录运行它。
注意
PostgREST要求在系统上安装libpq(PostgreSQL C库)。没有该库,您将收到类似“加载共享库时出错:libpq.so.5”之类的错误。这是解决方法:
Ubuntu或Debian
Fedora,CentOS或Red Hat
OS X
Windows
连接到容器内的SQL控制台(psql)。为此,请从命令行运行此命令:
sudo docker exec -it tutorial psql -U postgres
您应该看到psql命令提示符:
psql (9.6.3)
Type "help" for help.
postgres=#
我们要做的第一件事是为将在API中公开的数据库对象创建一个命名模式。我们可以选择任何喜欢的名称,然后选择“ api”。执行此操作,并在启动的psql提示中执行其他SQL语句。
create schema api;
我们的API将有一个终结点,/todos
该终结点来自一个表。
create table api.todos (
id serial primary key,
done boolean not null default false,
task text not null,
due timestamptz
);
insert into api.todos (task) values
('finish tutorial 0'), ('pat self on back');
接下来,创建一个角色以用于匿名Web请求。收到请求时,PostgREST将切换到数据库中的该角色以运行查询。
create role web_anon nologin;
grant usage on schema api to web_anon;
grant select on api.todos to web_anon;
该web_anon
角色有权访问api
架构中的内容,并有权读取todos
表中的行。
创建专用角色以连接到数据库是一种好习惯,而不是使用特权较高的postgres
角色。因此,我们将执行此操作,命名角色authenticator
,并授予他切换到该web_anon
角色的能力:
create role authenticator noinherit login password 'mysecretpassword';
grant web_anon to authenticator;
现在退出psql;是时候启动API了!
\q
PostgREST使用配置文件来告诉它如何连接到数据库。在其中创建一个文件tutorial.conf
:
db-uri = "postgres://authenticator:mysecretpassword@localhost:5433/postgres"
db-schema = "api"
db-anon-role = "web_anon"
配置文件还有其他选项,但这就是我们所需要的。现在运行服务器:
./postgrest tutorial.conf
你应该看到
Listening on port 3000
Attempting to connect to the database...
Connection successful
现在可以处理网络请求了。您可以使用许多不错的图形API探索工具,但是在本教程中,我们将使用curl
它,因为它可能已经安装在您的系统上。打开一个新终端(保留其中一个正在运行PostgREST的终端)。尝试对待办事项执行HTTP请求。
curl http://localhost:3000/todos
API回复:
[
{
"id": 1,
"done": false,
"task": "finish tutorial 0",
"due": null
},
{
"id": 2,
"done": false,
"task": "pat self on back",
"due": null
}
]
使用当前角色权限,匿名请求对todos
表具有只读访问权限。如果我们尝试添加新的待办事项,我们将无法执行。
curl http://localhost:3000/todos -X POST \
-H "Content-Type: application/json" \
-d '{"task": "do bad thing"}'
响应为401未经授权:
{
"hint": null,
"details": null,
"code": "42501",
"message": "permission denied for relation todos"
}
我们已经有了它,数据库之上的基本API!在接下来的教程中,我们将看到如何使用更复杂的用户访问控制以及更多的表和查询来扩展示例。
现在您已经运行了PostgREST。
在上述教程中,我们创建了一个带有单个端点的只读API以列出待办事项。我们可以采取许多方法来使该API更加有趣,但是一个不错的起点是允许一些用户在读取数据的同时更改数据。
上一教程web_anon
在数据库中创建了一个角色,用于执行匿名Web请求。让我们todo_user
为使用API进行身份验证的用户创建一个角色。该角色将有权对待办事项列表做任何事情。
-- run this in psql using the database created
-- in the previous tutorial
create role todo_user nologin;
grant todo_user to authenticator;
grant usage on schema api to todo_user;
grant all on api.todos to todo_user;
grant usage, select on sequence api.todos_id_seq to todo_user;
客户端使用JSON Web令牌向API进行身份验证。这些是JSON对象,使用仅我们和服务器已知的密码进行加密签名。因为客户端不知道密码,所以他们无法篡改其令牌的内容。PostgREST将检测伪造令牌并拒绝它们。
让我们创建一个密码并将其提供给PostgREST。想想一个不错的长篇文章,或使用一种工具生成它。您的密码必须至少包含32个字符。
注意
Unix工具可以为您生成一个不错的密码:
# Allow "tr" to process non-utf8 byte sequences
export LC_CTYPE=C
# read random bytes and keep only alphanumerics
< /dev/urandom tr -dc A-Za-z0-9 | head -c32
打开tutorial.conf
(在上一教程中创建的)并添加带有密码的行:
# PASSWORD MUST BE AT LEAST 32 CHARS LONG
# add this line to tutorial.conf:
jwt-secret = ""
如果PostgREST服务器仍在上一教程中运行,请重新启动它以加载更新的配置文件。
通常,您在数据库或另一台服务器中的自己的代码将创建并签名身份验证令牌,但是对于本教程,我们将“手工制作”一个。转到jwt.io并填写如下字段:
请记住填写您生成的密码,而不要输入“ secret”。填写密码和有效载荷后,左侧的编码数据将更新。复制编码的令牌。
注意
尽管令牌看起来很模糊,但对负载进行反向工程很容易。令牌只是签名的,没有加密的,所以不要把东西放进不想让确定的客户看到的东西。
回到终端,让我们使用curl
添加待办事项。该请求将包含一个包含身份验证令牌的HTTP标头。
export TOKEN=""
curl http://localhost:3000/todos -X POST \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"task": "learn how to auth"}'
现在,我们已经完成了待办事项列表中的所有三个项目,因此让我们将done
所有PATCH
请求都设置为true 。
curl http://localhost:3000/todos -X PATCH \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"done": true}'
待办事项的请求显示了其中三个,并且都已完成。
curl http://localhost:3000/todos
[
{
"id": 1,
"done": true,
"task": "finish tutorial 0",
"due": null
},
{
"id": 2,
"done": true,
"task": "pat self on back",
"due": null
},
{
"id": 3,
"done": true,
"task": "learn how to auth",
"due": null
}
]
目前,我们的身份验证令牌对所有永恒有效。只要服务器继续使用相同的JWT密码,服务器就会使用该令牌。
更好的策略是使用声明包含令牌的过期时间戳exp
。这是PostgREST特别对待的两个JWT主张之一。
要求 | 解释 |
---|---|
role |
在数据库角色下执行SQL请求API |
exp |
令牌的过期时间戳,以“ Unix epoch time”表示 |
注意
纪元时间定义为自1970年1月1日00:00:00协调世界时(UTC)起经过的秒数,减去此后发生的of秒数。
为了观察有效期,我们将exp
在未来的令牌中添加五分钟的索赔。首先找到从现在起五分钟的纪元值。在psql中运行以下命令:
select extract(epoch from now() + '5 minutes'::interval) :: integer;
返回jwt.io并将有效负载更改为
{
"role": "todo_user",
"exp": 123456789
}
注意:不要忘记将123456789
以上代码段中的虚拟纪元值更改为psql命令返回的纪元值。
像以前一样复制更新的令牌,并将其另存为新的环境变量。
export NEW_TOKEN=""
尝试在到期之前和之后以curl发出此请求:
curl http://localhost:3000/todos \
-H "Authorization: Bearer $NEW_TOKEN"
到期后,API返回未经授权的HTTP 401:
{"message":"JWT expired"}
撤销令牌
即使令牌过期,有时您还是可能想立即撤消对特定令牌的访问。例如,假设您了解到一个心怀不满的员工无济于事,而他的令牌仍然有效。
要撤销特定令牌,我们需要一种区别于其他令牌的方法。让我们添加一个email
与发出该令牌的客户端电子邮件匹配的自定义声明。
继续并使用有效载荷制作新令牌
{
"role": "todo_user",
"email": "[email protected]"
}
将其保存到环境变量:
export WAYWARD_TOKEN=""
PostgREST允许我们指定在尝试身份验证期间运行的存储过程。该函数可以执行其喜欢的任何事情,包括引发异常以终止请求。
首先创建一个新的架构并添加函数:
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.claim.email', true) =
'[email protected]' then
raise insufficient_privilege
using hint = 'Nope, we are on to you';
end if;
end
$$;
下次更新tutorial.conf
并指定新功能:
# add this line to tutorial.conf
pre-request = "auth.check_token"
重新启动PostgREST,以使更改生效。接下来,尝试使用我们的原始令牌提出请求,然后使用已撤销的令牌进行请求。
# this request still works
curl http://localhost:3000/todos -X PATCH \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"done": true}'
# this one is rejected
curl http://localhost:3000/todos -X PATCH \
-H "Authorization: Bearer $WAYWARD_TOKEN" \
-H "Content-Type: application/json" \
-d '{"task": "AAAHHHH!", "done": false}'
服务器以403禁止响应:
{
"hint": "Nope, we are on to you",
"details": null,
"code": "42501",
"message": "insufficient_privilege"
}