Django应用用户认证环节及限制多登录时is_authenticated恒为False问题
1. Django用户身份认证完成的环节
Django的用户身份认证逻辑由django.contrib.auth.middleware.AuthenticationMiddleware完成,该中间件会在请求处理阶段,从已解析的session中取出已登录用户的ID,查询对应的用户实例并赋值给request.user属性。因此要获取到已认证的request.user,自定义中间件必须放在AuthenticationMiddleware的执行顺序之后,你当前的中间件配置顺序是符合要求的。
2. 问题原因与调整方案
你遇到request.user.is_authenticated始终为False,还有逻辑本身的缺陷,主要有以下几个原因:
- 你当前的逻辑是在请求处理阶段校验session,但用户首次登录时,
auth.login()方法执行完成前request.user还是匿名用户,auth.login()执行后才会生成session_key、给request.user赋值为已认证用户,此时请求阶段的中间件已经执行完毕,首次登录时你无法存储对应的session_key - 代码本身存在语法问题:
__str__方法、if user.session_key:行的缩进错误,filter查询不到结果会返回None不会抛异常,不需要用try-except包裹 - 未对登录接口做白名单过滤,会导致登录请求本身直接被拦截
调整后的实现逻辑
第一步:新增登录/登出信号监听器
在你的应用models.py末尾新增如下代码,登录成功后直接写入session_key,避免中间件请求阶段拿不到刚登录的用户信息:
from django.contrib.auth.signals import user_logged_in, user_logged_out from django.dispatch import receiver @receiver(user_logged_in) def handle_user_login(sender, request, user, **kwargs): # 登录成功后直接更新session_key LoggedInUser.objects.update_or_create( user=user, defaults={"session_key": request.session.session_key} ) @receiver(user_logged_out) def handle_user_logout(sender, request, user, **kwargs): # 登出后清空session_key LoggedInUser.objects.filter(user=user).update(session_key=None)
第二步:调整中间件逻辑
from django.http import JsonResponse def MySessionMiddleware(get_response): def my_function(request): # 登录接口加白名单,避免拦截正常登录请求 if request.path == "/替换为你的实际登录接口路径/": return get_response(request) if request.user.is_authenticated: user_session = LoggedInUser.objects.filter(user=request.user).first() if user_session and user_session.session_key: if user_session.session_key != request.session.session_key: return JsonResponse( "you already have a active session kindly logout from that", status=400, safe=False ) response = get_response(request) return response return my_function
额外注意点
如果使用Django默认的auth登录视图,白名单路径替换为/accounts/login/即可;可定期清理Django session表中已过期的session,同步删除对应LoggedInUser的无效session_key记录,避免误拦截。
内容的提问来源于stack exchange,提问作者Dhruv Singhal
相关产品推荐
相关产品推荐

