我正在开发一个基于令牌的api网关。它基本上为真实客户提供令牌。所以我不确定如何删除过期的令牌。对于每个请求,我检查了令牌是否有效。
Option 1 is
Mark status of token as expired in database table row.
and create a scheduler to run in midnight to delete expired tokens.
Option 2 is
Delete the token from the row when its expired.
In here No need to run a scheduler.
通常,此API网关每秒将处理大约1000个请求,并且这将逐日增加。
所以我不确定应该使用哪个选项。
我使用的技术是。 Spring mvc,Spring数据jpa和Postgre DB。将部署在tomcat服务器上。
答案 0 :(得分:1)
这两个选项都不是特别好,因为它们都会修改表格行,从而生成I / O.在1000 q / s时,您需要更好的解决方案。在行级安全性的上下文中,2ndQuadrant是blog post on authenticating users through connection pooling。博客文章有一些问题恕我直言和非相关材料,所以我会尝试以正确的方式重做它(或阅读我在那里的博客文章评论)。
在Java中 - 与大多数其他编程语言和/或框架一样 - 出于性能原因,连接池是连接到数据库服务器的首选方式。有一个隐式契约,应用程序从池中请求Connection
实例,使用它然后将实例返回到池以获取其他某个线程。坚持Connection
不是一种选择,因为它打破了池逻辑。所以请按以下步骤操作:
连接池对象
使用数据库群集凭据创建连接池对象。该角色应该GRANT
对表和其他对象的所有必要权限。
<强>验证强>
在应用程序中,用户使用池中的myapp_login(username, password)
对Connection
或类似内容进行身份验证。在数据库中,将根据表users
或您在设置中调用的任何内容来检查凭据。如果找到匹配项,则创建一个随机令牌并将其插入表中:
CREATE UNLOGGED TABLE sessions (
token text DEFAULT uuid_generate_v4()::text,
login_time timestamp DEFAULT CURRENT_TIME,
user_name integer,
...
);
根据需要添加任意数量的字段。我在这里使用uuid
(强制转换为text
,继续阅读),但您也可以md5()
部分数据或使用某些pg_crypto
例程。
此表必须快,因此它是UNLOGGED
。这意味着它不是崩溃安全的,并且会在发生某些服务器错误后被截断,但这不是问题:无论如何所有数据库会话都将失效。此外,不要在表上放置任何约束,例如NOT NULL
,因为对此表的唯一访问是通过您作为开发人员设计的函数,没有普通用户触及此表,并且每个约束都涉及更多的CPU周期。
myapp_login()
函数看起来有点像这样:
CREATE FUNCTION myapp_login(uname text, password text) RETURNS text AS $$
DECLARE
t text;
BEGIN
PERFORM * FROM app_users WHERE username = uname AND pwd = password;
IF FOUND THEN
INSERT INTO sessions(user_name) VALUES (uname) RETURNING token INTO t;
EXECUTE format('SET SESSION "my_app.session_user" TO %s', t);
RETURN t;
END IF;
SET SESSION "my_app.session_user" = '';
RETURN NULL;
END;
$$ LANGUAGE plpgsql STRICT SECURITY DEFINER;
REVOKE EXECUTE ON FUNCTION myapp_login(text, text) FROM PUBLIC;
GRANT EXECUTE ON FUNCTION myapp_login(text, text) TO myapp_role;
如您所见,token
也在SET SESSION
的环境变量中设置(需要文字文字值,因此uuid::text
广告和EXECUTE
命令)然后返回给调用者。该会话令牌应该存储在Java端的应用程序代码中的某个位置。
该函数对app_users
表和INSERT
表上的sessions
进行查找。第一个是便宜的,第二个是昂贵的。
恢复相同的会话以进行进一步的查询
如果您的应用用户在第一次查询后需要进一步访问数据库,那么请再次从连接池中获取Connection
实例,但不要调用myapp_ login()
而是myapp_resume(token)
。后一个函数在sessions
表(廉价)中查找令牌,如果找到,则将会话变量设置为此新令牌。您还可以检查login_time
值是最近值还是使用CURRENT_TIME
进行设置,以保持会话“活跃”(昂贵)或执行任何其他必要的业务。
诀窍是让会话尽可能精简,因为这可能会在会话期间多次发生(从应用程序的角度来看)。
关闭会话
当您的应用用户完成后,执行myapp_logout(token)
删除sessions
表中与该令牌对应的行。
未正确关闭的会话不会从sessions
表中删除,但我不会过分担心。您可以安排一个每周运行一次的作业来删除所有超过6的行几个小时左右。这也可以让你弄清楚错误的来源,例如。
关于token
的最后一句话。 uuid
只是一个随机数,但您也可以使用一些随机数据来创建应用程序用户名的哈希,并使用它,例如,在RLS或其他一些基于行的访问机制中;我链接到上面的博客文章有很好的信息。在我自己开发的应用程序中,我将users
表中的行链接到允许用户查看的内容。在任何一种情况下你都应该权衡专家和骗局:可以在RLS中使用的哈希听起来不错,但它需要重新计算哈希值(这往往很昂贵)并与每个查询的会话哈希进行比较,对用户表的重复查找也是一种开销。设置另一个可以在查询时使用current_setting()
检查的会话变量可能是一个不错的选择。
答案 1 :(得分:0)
我认为最简单的方法就是这样。在数据库中生成令牌时,可以存储生成时间。因此,当客户端向您的数据库发送请求时,您可以检查它是否已过期并在请求时将其删除。