我正在开发基于令牌的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 Data JPA和Postgre DB将部署在tomcat服务器上。
最佳答案
这两个选项都不是特别好,因为这两个选项都会修改表行并因此生成I / O。以1,000 q / s的速度,您需要一个更好的解决方案。在行级安全性的上下文中,2ndQuadrant是blog post on authenticating users through connection pooling。该博客文章还存在一些恕我直言和不相关的问题,因此我将尝试以正确的方式在此处重做(或在那儿阅读我对博客文章的评论)。
与大多数其他编程语言和/或框架一样,在Java中,出于性能方面的考虑,连接池是连接数据库服务器的首选方式。有一个隐式约定,即应用程序从池中请求Connection
实例,使用该实例,然后将该实例返回到池中用于其他线程。坚持使用Connection
是不可行的,因为它破坏了合并逻辑。因此,请执行以下操作:
连接池对象
创建具有数据库群集凭据的连接池对象。应该对该角色授予表和其他对象上的所有必需特权。
认证方式
在应用程序中,用户使用池中的GRANT
对执行myapp_login(username, password)
或类似操作进行身份验证。在数据库中,将根据表Connection
或在您的设置中调用的凭据检查凭据。如果找到匹配项,则创建一个随机令牌并将其插入表中:
CREATE UNLOGGED TABLE sessions (
token text DEFAULT uuid_generate_v4()::text,
login_time timestamp DEFAULT CURRENT_TIME,
user_name integer,
...
);
根据需要添加任意多个字段。我在这里使用
users
(广播到uuid
,请继续阅读),但是您也可以text
一些数据或使用某些md5()
例程。该表必须快速,因此为
pg_crypto
。这意味着它不是崩溃安全的,并且会在某些服务器错误后被截断,但这不是问题:无论如何,所有数据库会话都将失效。另外,请勿在表上放置任何约束,例如UNLOGGED
,因为对该表的唯一访问是通过您作为开发人员设计的功能进行的,没有普通用户接触过此表,并且每个约束都涉及更多的CPU周期。NOT NULL
函数看起来像这样: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;
如您所见,
myapp_login()
也在token
的环境变量中设置(它需要文字文本值,因此需要SET SESSION
强制转换和uuid::text
命令),然后返回给调用方。该会话令牌应存储在Java端应用程序代码中的某个位置。该函数在
EXECUTE
表上进行查找,并在app_users
表上进行查找。第一个很便宜,第二个很贵。恢复同一会话以进行进一步查询
如果您的应用程序用户在第一次查询后需要进一步的数据库访问权限,请再次从连接池中获取一个
INSERT
实例,但不要调用sessions
而是调用Connection
。后一个函数在myapp_ login()
表中查找令牌(便宜),如果找到,则将会话变量设置为此新令牌。您还可以检查myapp_resume(token)
值是否为最新值,或使用sessions
进行设置,以保持会话“有效”(昂贵)或进行任何其他必要的业务。诀窍是使会话尽可能保持精简,因为这很可能在会话期间多次发生(从应用程序的角度来看)。
关闭会议
应用用户完成后,请执行
login_time
,这将从CURRENT_TIME
表中删除与令牌相对应的行。未正确关闭的会话不会从
myapp_logout(token)
表中删除,但我对此不必太担心。您可以安排每周运行一次的作业来删除所有早于6个小时左右的行。例如,这也可以让您找出错误的来源。sessions
上的最后一个词。 sessions
只是一个随机数,但是您也可以使用一些随机数据对应用程序用户名进行哈希处理,并在例如RLS或其他基于行的访问机制中使用它。我上面链接到的博客文章对此有很好的信息。在我自己开发的应用程序中,我将token
表中的行链接到允许用户查看的内容。无论哪种情况,您都应该真正权衡利弊:可以在RLS中使用的哈希值听起来不错,但它需要重新计算哈希值(这往往很昂贵),并与每个查询的会话哈希值进行比较,对用户表的重复查找也是一项开销。设置另一个可以在查询时使用uuid
检查的会话变量可能是一个不错的选择。
关于java - 从数据库中删除 token 的最佳方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37907542/