根据Persistent Login Cookie Best Practice,你绝不允许多次使用“记住我”的标记:
持久性cookie适用于单次登录。确认身份验证后,用于登录的随机数将失效,并分配一个全新的cookie。标准会话管理处理会话生命周期的凭证,因此在下一个会话之前不会检查新分配的cookie(此时它也会在使用后失效)。
然后,您如何处理用户同时访问您网站上多个网址的竞争条件?我现在实际上遇到了这个问题。
假设有两个请求同时从浏览器发送到服务器。请求不包含会话cookie,但包含相同的“记住我”cookie。其中一个请求将在另一个之前处理,并将通过经过身份验证的会话cookie和重新生成的“记住我”cookie获得响应。
第二个请求中的“记住我”令牌现已失效,并在服务器上生成另一个会话ID。此请求失败,因为无法对用户进行身份验证。
我想出了一些可能的解决方案,但它们似乎都不是很好。我错过了什么吗?
答案 0 :(得分:2)
老问题,但我没有在任何地方找到答案。 我有同样的问题。我的解决方案是将旧令牌存储在数据库中,如果找不到主令牌,则将其用作后备。但是我确保旧令牌仅在短时间内有效,比如令牌更改后的几秒钟。然后我只在前一次更新后经过一段时间才更改令牌,否则会出现令牌连续多次更改的情况。
答案 1 :(得分:1)
要详细说明vangoz的答案,我想补充一点就是必须采用某种锁定机制。在这里考虑这个PHP-ish伪代码:
if (isFallback($token)) {
// Log the user in
} else {
// Usual processing
// If token has to be updated, save old token as fallback for a few seconds
}
当存在并发请求时,它们可能都会在else分支中结束,一个请求将导致更新,另一个请求将导致令牌失效。我解决这个问题的方法是使用一个以$token
命名的命名锁来包装else分支。此外,所有并发请求,但一个将无法获取锁定,在这种情况下,我们睡了一会儿并重试(重试时我们会发现令牌已成为后备令牌)。
if (isFallback($token)) {
// Log the user in
} else {
$couldLock = lock($token);
if (!$couldLock) {
usleep(10000);
// Retry, possibly a recursive call
} else {
// Usual processing
// If token has to be updated, save old token as fallback for a few seconds
unlock($token);
}
}
我希望这些考虑因素有用。