RESTful 身份验证 - 导致高负载时性能不佳?
Posted
技术标签:
【中文标题】RESTful 身份验证 - 导致高负载时性能不佳?【英文标题】:RESTful authentication - resulting poor performance on high load? 【发布时间】:2011-07-09 03:28:53 【问题描述】:对于 RESTful Web 服务,我们说服务器不应存储任何状态。现在,对于每个请求,“用户”都必须经过身份验证,并且必须获得他/她希望执行的操作的授权。
现在每个请求都将包含该用户的授权数据。以下是我的困惑:
假设主页上有一个登录名和密码字段。用户输入发送回服务器的用户名/密码,用户验证,然后返回“一些令牌”。现在这个令牌在每次请求时都会发送到服务器。问题:
后端数据库是否需要单独的表来存储这些 按用户名索引的令牌? 假设令牌存储在数据库中,那么每个请求都需要进行数据库调用。这不会使数据库服务器成为高负载时的瓶颈吗? 如果令牌没有真正存储在数据库中,那么最好的“安静”存储位置是什么? 拥有会话可能并不平静,但我看不到宁静的身份验证/授权如何扩大(w.r.t. 以上几点)? 如果它不是令牌,那么是否需要来回发送用户名/密码? (听起来是个坏主意:)我可能误解了 RESTful 身份验证/授权的概念。但是,对于每个 http 请求,“服务”是否真的需要访问数据库以验证凭据?有什么东西可以缩短这个过程并且仍然坚持宁静的原则吗?我可以考虑拥有一个存储详细信息的缓存,并且在服务器重新启动的情况下,它只会访问数据库。这只是可能使系统复杂化的性能优势(也许值得,不知道)。这是唯一的解决方案吗?
那么从 REST 的理论/概念的角度来看(不是必要的实现),这个问题是如何处理的(如果它是一个问题的话)?在您的专业经验中,您是如何处理这个问题的?这种方法的 Restful 程度如何?
我们正在开发 Restlet+J2EE+mysql Restful Web 服务,我弹出了这个问题,但没有令人满意的答案(Google、*** 等)我知道 HTTP 的基本和摘要授权,但我按照上面的解释,不熟悉存储/检索的内部结构。
【问题讨论】:
一般准则:使用磁盘/数据库的次数越少,应用程序可以实现的负载就越高。 【参考方案1】:您仅在登录时验证凭据(例如用户名/密码)。当用户成功登录时,您会向他们发送一个包含无法猜测的“会话 ID”的 cookie。此会话 ID 成为令牌,在检查凭据后允许进一步访问。服务器应用程序通过在某个内存数据结构中查找令牌来验证令牌。令牌会在一段合理的时间后过期,以防止内存耗尽。如果用户明确注销(按下“注销”按钮),则令牌会从内存存储中明确删除。
在发送和接收令牌时使用 https 很重要——否则嗅探令牌会使黑客“劫持会话”。换句话说,从登录到注销的整个应用程序工作流程都使用 https。
【讨论】:
@Health : 这个策略会在服务器重启后存活吗?内存中的数据结构是否也需要存储在持久存储中(回到方框 1)?那么实际上不只是我提到的缓存吗? 所以它只是一个普通的用户会话,使用 JSESSIONID cookie?也许 OP 认为这是一种状态形式,因此不属于 RESTful 架构? 不,它不会在重新启动后继续存在,除非您在重新启动时坚持它。处理这个问题的一种方法是不用担心,不要经常重启服务器,并要求用户重新登录,而不是在重启后继续会话。 @G_H -- 如果您需要经过身份验证的用户,并且您不想重新验证每个请求,您将如何在零状态下完成呢? OP 的问题是他认为 RESTful 是他的首要任务,而维护会话状态也是他的首要任务。 确保你能在重启后存活下来,在后台保持一个(或几个)线程保持状态,在会话卸载期间显式保持; tomcat 默认支持会话加载/卸载,但不包括服务器崩溃。【参考方案2】:RESTful 服务应该是无状态的,所以是的,客户端每次调用 RESTful 服务时都需要提供身份验证凭据。
用某种散列或令牌或会话 ID 替换用户名/密码并没有真正的好处。所有这些都只是不同形式的身份验证,必须针对持久存储中的一些数据进行验证。会话和令牌的缺点是它们违反了无状态要求。
如果数据库性能有问题,请使用 memcached 或其他一些高性能数据缓存。您可能会发现您的所有数据库访问都受益于此,而不仅仅是检索身份验证凭据。
最后,只要您通过 HTTPS 发送用户名/密码,每次都没有问题。永远不要通过纯文本 HTTP 发送重要的身份验证凭据。
【讨论】:
每次发送 U/P 肯定有一个缺点:客户端必须保留它而不是一旦它传递到服务器就可以丢弃它,从而降低客户端的安全性.在某些方案中,这可能不太安全。另一个缺点是速度:验证令牌或 cookie 可能比验证 U/P 更快,特别是如果您使用 bcrypt 之类的密码哈希正确加密了密码。【参考方案3】:REST 的精神是无状态。这并不意味着客户端状态不能被服务持久化,但实际上它确实意味着客户端状态保存在内存中被服务器通常是一件坏事。
与其将身份验证data保存在内存中,或者每次都去DB进行验证,你可以做的是在内存中保存一个函数(即代码)用于加密/解密用户信息。这是一个也被建议的技术:
What should I store in cookies to implement "Remember me" during user login
因此,例如,您将执行以下操作:
-
当客户端第一次联系服务时,它没有 cookie。
您发出一个包含用户信息的 cookie,并使用您的函数“签名”它(所有运行您的代码的服务器都可以这样做)
当客户端再次联系服务时,你检查它是否有cookie;如果没有,请重复 (2)。
但是,如果它确实有一个 cookie,您尝试解密(同样,使用在您的基础架构中复制的单个函数)并验证您是否可以解包和消化该用户 ID 信息。
这将验证用户并为您提供身份信息,所有这些都无需过多地访问数据库。而且它是 RESTful。
请记住,我描述的这个“功能”并不是一个新事物——它是一个标准的安全散列,而是一个基于只有您的集体服务器知道的唯一私钥的散列。并且您可以根据需要轮换这样的密钥等。
【讨论】:
@kvista:好点。我在想同样的“概念”,但不能说得这么清楚。因此,如果这解决了身份验证问题,那么从某种意义上说,同样的技术是否也可以“外推”用于授权?这在某种意义上是持久存在于数据库中的“业务规则”的概念——我不确定为授权做同样的事情是否是一件好事。想法? @Nupul:在你的“加密 cookie 值”中,你可以存储任何你想要的东西——用户名、ID、身份验证信息(角色)等等。所以是的,我认为这是可能的;取决于身份验证信息在多大程度上是数据驱动的(在这种情况下,在数据库中查找很有用)。顺便说一句,请记住,对于整个 cookie,查看过期问题很重要;例如,删除帐户的用户应该删除他们的 cookie。 @kvista:关于删除的好点。在我们的系统中,用户无法删除帐户,但系统管理员可以 - 通过转到数据库 :) 假设我在时间 = t 删除帐户,cookie 将在 t+5 到期,但用户现在删除了谁的帐户尝试在 t+2 时使用“cookie”访问系统 - 然后呢?我们应该依靠外键约束失败吗?有些可能不会! @Nupal:我认为您需要就是否优化身份验证和授权做出一般性决定。您最初的问题是关于是否有可能的“捷径”,除了每次都验证凭据之外。这是一个公平的问题。但是让我们首先记住,我们已经做出了决定,我们已经做出了优化的决定。在这种情况下,您有几个选择。首先,您可能想要轮换解密函数。但是,对于轮换之前的删除,您可能可以在用户内存中维护一个黑名单以禁止。更多内容如下 @Nupal(续):就我个人而言,我一般不喜欢黑名单的想法;我的建议假设了 Web 上的常见情况——删除非常罕见,即使在需要删除的几次时也可以由用户驱动(您可以手动使 cookie 过期)。另一种选择是在 30 分钟不活动后使 cookie 过期,这意味着管理员可以随时删除用户,并且“下次”将不允许该用户进入(在任何当前会话空闲 30 分钟后)。对于您的应用类型,这可能是允许的。这真的取决于 - 但请记住优化的复杂性成本。以上是关于RESTful 身份验证 - 导致高负载时性能不佳?的主要内容,如果未能解决你的问题,请参考以下文章
使用 JWT 令牌时应该如何处理 RESTful 身份验证?