Django会话cookie因多个并发请求丢失

2 投票
2 回答
2119 浏览
提问于 2025-04-16 10:31

我正在开发一个应用程序,客户端会不时地向服务器发送请求(我们暂时不讨论原因)。当服务器处理这些请求时,它会检查客户端是否已经登录,方法是使用 request.user.is_authenticated()

大致是这样的:

def handle_ping_request(request):
    if request.user.is_authenticated():
       # Do something...
    else:
       # Do Something else...

我注意到,有时候服务器会收到一个登录请求,紧接着又收到同一个用户的 ping 请求。此时,客户端已经成功登录,服务器的响应会返回一个新的会话 ID(对应已登录的用户),而我猜测旧的会话 ID(对应未登录的用户)会被删除。当服务器处理 ping 请求时,这个请求里包含的是旧的会话 ID。因此,ping 请求的响应会返回一个第三个会话 ID,而在客户端下一个请求时,客户端就会发现自己已经不再登录了。

我的登录代码大概是这样的:

if not request.user.is_authenticated():
    user = auth.authenticate(...credentials...)

    if user and user.is_active:
        auth.login(request, user)

你有什么建议可以避免这个问题吗?最好是不需要客户端做什么。

谢谢。

2 个回答

0

你可以创建一个替代标准的 contrib.auth.login 方法,让它保持相同的会话 ID,而不是生成一个新的。这可以通过使用一个自定义的身份验证后端来实现,这个后端不会生成新的密钥,或者通过创建一个自定义的会话后端,重写 contrib.sessions.base 中的 cycle_key() 方法 来重复使用相同的密钥。

但是:想想看,重复使用相同的会话密钥可能会带来什么风险——根据这个系统的使用场景,你可能会让自己更容易受到会话劫持的攻击(也就是说,只有一个会话 ID 可以被窃取),还有可能出现缓存问题,导致缓存返回未授权页面的内容,而不是授权页面的内容,因为会话 ID 在技术上是相同的,缓存无法区分这两种情况,等等等等。

简而言之:这就是为什么默认情况下,它是这样设计的原因。

1

在服务器上处理这个问题可能会很麻烦,因为你需要建立一种信号系统,来判断当前的请求是否来自正在进行身份验证的客户端。我建议的办法是,简单地修改客户端的代码,让它在等待登录请求的回复时,不去发送请求。

撰写回答