从数据库中删除令牌的最佳方法

时间:2016-06-19 13:03:23

标签: java spring postgresql jpa

我正在开发一个基于令牌的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服务器上。

2 个答案:

答案 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)

我认为最简单的方法就是这样。在数据库中生成令牌时,可以存储生成时间。因此,当客户端向您的数据库发送请求时,您可以检查它是否已过期并在请求时将其删除。