Django 请求 session 未与登录用户模型链接

标签 django python-3.x django-sessions

尽管为有效的用户名和密码组合调用身份验证和登录,但当移动到另一个 View 时, session 无法链接到用户的用户模型。该模型改为“AnonymousUser”,他们无法进步,因为要求他们是有效的登录用户。

我有一个运行良好的系统已经有一段时间了(我想每个人在遇到问题之前都会这么说......)为了避免必须向每个 View 添加“require_login”装饰器,我制作了一个中间件检查用户是否已登录,如果未登录,应将他们重定向到登录页面。

此设置已经运行了一段时间,但是,最近对用户模型的更改表明,迁移前存在的用户不再能够登录。令人困惑的是,如果他们有自己的密码通过 django 管理员更新,他们可以正常访问网站

认证 View

import django.http
from django.shortcuts import render, redirect
from django.contrib.auth import authenticate, login, logout, update_session_auth_hash, models as admin_models

from django.conf import settings

from .models import LoginAttempts

import logging
logging.basicConfig(level=logging.DEBUG)
log = logging.getLogger(__name__)

# Define the alert to be given to the users if they have passed invalid user credentials
INVALID_LOGIN = {
    ...
}

LOCKED_ACCOUNT = {
    ...
}

def login_form(request):
    """ Return the login page """

    if request.POST: # Handle the form submission

        # Extract the user information + attempt to authenticate the user
        username, password = request.POST.get("username"), request.POST.get("password")
        log.debug("user '{}' attempting to log in".format(username))

        # Collect the user account corresponding to the username passed
        accountQuery = admin_models.User.objects.filter(username=username)
        if accountQuery.exists() and password is not None:
            userModel = accountQuery.first()
            if userModel.loginattempts.isLocked:
                log.debug("'{}'s' account has been locked due to repeated failed attempts".format(username))
                request.session["alerts"].append(LOCKED_ACCOUNT)
                return render(request, "login.html")
        else:
            log.debug("'{}'s username doesn't exist or no password provided".format(username))
            request.session["alerts"].append(INVALID_LOGIN)
            return render(request, "login.html")

        # Authenticate the user/password combination
        user = authenticate(request, username=username, password=password)

        if user is not None:  # The user has been authenticated, log them in and redirect to the index page
            log.debug("User {} has been verified - logging user in".format(username))
            login(request, user)
            userModel.loginattempts.attempts = 0
            userModel.save()
            return django.http.HttpResponseRedirect("/")
        else:
            log.debug("User {} failed to authenticate".format(username))
            request.session["alerts"].append(INVALID_LOGIN)
            userModel.loginattempts.attempts += 1
            if userModel.loginattempts.attempts >= 10: userModel.loginattempts.isLocked = True
            userModel.save()

    return render(request, "login.html")

中间件

class RequireLogin:
    """ Require that the user be logging in to view the pages - avoiding the requirement
    to declare "@login_required" on all views
    """

    def __init__(self, get_response: callable):
        self.get_response = get_response  # Function passes the request through and fulfils and collects the generates response

    def __call__(self, request):
        if request.path_info != settings.LOGIN_URL and not request.user.is_authenticated:
            return HttpResponseRedirect(settings.LOGIN_URL)

        return self.get_response(request)

日志

如果在中间件的if语句中我们添加打印语句来打印第一个条件(我知道必须为真...)请求,请求用户模型,请求用户模型is_authenticated。我们从服务器得到以下 react :

terminal

显然,该函数已确定用户可以登录并将他们重定向到索引页面,但是,当他们请求索引页面时,他们未被 session 识别为已登录并被重定向回。

这是 Django session token 的图像,当尝试为单个用户登录时,它实际上只更新一行:

database

为了回答大牛,这里是按顺序排列的中间件列表:

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
    "ace_main.middleware.RequireLogin",
    "ace_main.middleware.RestrictedAccessZones",  # For certain areas - extra restriction - don't worry about this
    "ace_main.middleware.RemoveAlerts",  # Alert structure dequeue alerts already shown to the user
    "ace_main.middleware.UserLogging",  # track the user around the site 
]

最佳答案

我建议不要“完全覆盖身份验证过程”......最好是:


from django.contrib import messages
from django.contrib.auth.backends import ModelBackend


class SophisticatedModelBackend(ModelBackend):
    def authenticate(self, request, username=None, password=None, **kwargs):
        user = super(self, SophisticatedModelBackend).authenticate(request, username=username, password=password, **kwargs)
        if user:
            user = self.check_user_locked(request, user, **kwargs)
        else:
            messages.add_message(request, messages.ERROR, INVALID_LOGIN)
        user = self.postprocess_login_attempt(request, user, username, **kwargs)
        return user

    def check_user_locked(self, request, user, **kwargs):
        if user.loginattempts.isLocked:
            # I'd also recommend to also use django.contrib.messages instead of changing request.session
            messages.add_message(request, messages.ERROR, LOCKED_ACCOUNT)
            return None
        return user

    def postprocess_login_attempt(self, request, user, username, **kwargs):
        if user:
            user.loginattempts.attempts = 0
            user.save()
        else:
            userModel = admin_models.User.objects.filter(username=username).first()
            if userModel:
                userModel.loginattempts.attempts += 1
                if userModel.loginattempts.attempts >= 10: userModel.loginattempts.isLocked = True
                userModel.save()

        return user

  • 不要忘记设置或更新 settings.AUTHENTICATION_BACKENDS

附言以上所有更像是“最佳实践”。如果您只是想让您当前的代码正常工作,那么您可以试试这个:

 -            login(request, user)
 -            login(request, user, 'django.contrib.auth.backends.ModelBackend')

为什么这会起作用:AuthenticationMiddleware 调用 auth.get_user,它会:

    try:
        user_id = _get_user_session_key(request)
        backend_path = request.session[BACKEND_SESSION_KEY]
    except KeyError:
        pass

并且 request.session[BACKEND_SESSION_KEY]auth.login 期间设置为第三个参数,在您的情况下等于 None。所有这些只能在 django 代码的源代码中看到,所以最好使用默认的 auth.LoginViewauth.AuthenticationForm -> 这样你就不会错过任何东西关键。

关于Django 请求 session 未与登录用户模型链接,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55733803/

相关文章:

django - 如何在django模板中检查用户是否在线?

python - django session 以防止用户多次投票

django - 启动 Celery worker 进行生产。在 Azure/linux 应用服务上使用 Django/Python

django - 如何启动现有的 Django 项目?

python - 运行 python manage.py shell 我得到错误 : DLL load failed: The specified module could not be found.

django - 我可以用 Django 渲染 VueJS 吗?

python-3.x - 从命令提示符执行脚本时无法导入opencv

python - 从字符串中删除多个字符(形成一个单词)

python - 基于 str.contains 隔离相邻的列