Django HTTP 响应始终设置 `sessionid` cookie 并且会话数据不持久

问题描述 投票:0回答:1

我创建了一个自定义后端和相关中间件,用户登录的唯一条件是 ID_TOKEN cookie 与请求一起传递。

我的代码广泛基于

django.contrib.auth.backends.RemoteUserBackend
及其相关中间件
middleware django.contrib.auth.middleware.RemoteUserMiddleware

这是我的中间件:

import hashlib

from django.conf import settings
from django.contrib import auth
from django.core.exceptions import ImproperlyConfigured, PermissionDenied
from django.urls import reverse
from django.utils.deprecation import MiddlewareMixin
from main.auth.backends import MyRemoteUserBackend
from main.auth.jwt import JWT

logger = logging.getLogger(__name__)


class AllowAllUsersMiddleware(MiddlewareMixin):

    def process_request(self, request):
        if not hasattr(request, "user"):
            raise ImproperlyConfigured(
                "Requires 'AuthenticationMiddleware' in middleware"
            )

        try:
            id_token = request.COOKIES["ID-TOKEN"]
        except KeyError:
            if request.user.is_authenticated:
                self._remove_invalid_user(request)

            return

        token = JWT(id_token)
        id_token_digest = hashlib.sha256(id_token.encode(), usedforsecurity=False).hexdigest()
        has_id_token_digest_changed = id_token_digest != request.session.get("id_token_digest", "")

        if request.user.is_authenticated:
            if request.user.get_username() == token.username and not has_id_token_digest_changed:
                return  # username and token did not change, do nothing

            self._remove_invalid_user(request)

        user = auth.authenticate(request, token=token)

        if user:
            request.user = user
            auth.login(request, user)
            request.session["id_token_digest"] = id_token_digest

    def _remove_invalid_user(self, request):
        auth.logout(request)

这是我的后端:

from typing import Optional

from django.conf import settings
from django.contrib.auth import get_user_model
from django.contrib.auth.backends import ModelBackend
from django.contrib.auth.models import Group
from main.auth.jwt import JWT

UserModel = get_user_model()


class MyRemoteUserBackend(ModelBackend):
    def authenticate(self, request, token: JWT) -> Optional[UserModel]:
        if not token:
            return

        user, _ = UserModel._default_manager.get_or_create(username=token.username)

        return user

我的制作设置如下:

AUTHENTICATION_BACKENDS = [
    "main.auth.backends.MyRemoteUserBackend",  # custom backend
]

MIDDLEWARE = [
    "django.middleware.security.SecurityMiddleware",
    "django.contrib.sessions.middleware.SessionMiddleware",
    "django.middleware.locale.LocaleMiddleware",
    "django.middleware.common.CommonMiddleware",
    "django.middleware.csrf.CsrfViewMiddleware",
    "django.contrib.auth.middleware.AuthenticationMiddleware",
    "main.auth.middleware.AllowAllUsersMiddleware",  # custom middleware
    "django.contrib.messages.middleware.MessageMiddleware",
    "django.middleware.clickjacking.XFrameOptionsMiddleware",
]

SESSION_COOKIE_SECURE = True
SESSION_COOKIE_SAMESITE = "Strict"

处理自定义会话数据在本地运行良好(我使用另一个自定义中间件注入 ID-TOKEN 的请求 cookie)。 在生产中,

sessionid
cookie 在每次请求/响应后都会重置。从我在 Firefox 网络选项卡中看到的情况来看,
set-cookie
标头始终与 HTTP 响应一起发送,导致会话数据丢失(名为“id_token_digest”的会话条目)。

这是一个

set-cookie
生产示例:
set-cookie sessionid=rlc...tn; expires=Mon, 03 Feb 2025 14:29:53 GMT; HttpOnly; Max-Age=1209600; Path=/; SameSite=Lax; Secure

以前有人处理过这样的问题吗?我的中间件洋葱分层是否错误?

django session cookies amazon-cloudfront django-middleware
1个回答
0
投票

由于中间件中的以下行,会话正在刷新:

if request.user.is_authenticated:
    if request.user.get_username() == token.username and not has_id_token_digest_changed:
        return  # username and token did not change, do nothing

    self._remove_invalid_user(request)

self._remove_invalid_user(request)
的调用是问题所在,因为这会注销用户,然后出于安全目的刷新会话。既然你提到你在引用
RemoteUserMiddleware
对应的行之后开发了这个,那么看起来如下:

if request.user.is_authenticated:
    if request.user.get_username() == self.clean_username(username, request):
        return self.get_response(request)
    else:
        # An authenticated user is associated with the request, but
        # it does not match the authorized user in the header.
        self._remove_invalid_user(request)

请注意,在这种情况下,仅当远程用户的用户名与登录用户的用户名不匹配时才会调用

_remove_invalid_user
。在您的情况下,即使用户名匹配但 ID 令牌不匹配,您也会调用该方法。

您似乎试图通过检查 ID 令牌的哈希值来检查 ID 令牌是否已更改,鉴于 ID 令牌是通常没有很长有效期的 JWT,这似乎没有多大意义。更改后的令牌很可能是针对同一用户身份的,并且只是过期令牌的替换。如果您想确认令牌是针对同一用户的,您应该使用令牌中的一些标识声明,例如

sub
声明等。现在假设您的用户名唯一标识用户,您可以按如下方式更改这些行:

if request.user.is_authenticated:
    if request.user.get_username() == token.username:
        return  # username did not change, do nothing
    else:
        self._remove_invalid_user(request)
© www.soinside.com 2019 - 2024. All rights reserved.