admin管理员组文章数量:1122832
Django derives the session key from the primary key in the User model:
.2/_modules/django/contrib/auth/
request.session[SESSION_KEY] = user._meta.pk.value_to_string(user)
We can also see that Django takes the session key and casts it to the primary keys type using:
get_user_model()._meta.pk.to_python(request.session[SESSION_KEY])
I want to change the session key from the user's name to the id I am using in my backend.
However, if I change the primary key on my user model, I will break active sessions that are using the user name as the session key.
How might I change the user field without breaking active sessions?
Django derives the session key from the primary key in the User model:
https://docs.djangoproject.com/en/3.2/_modules/django/contrib/auth/
request.session[SESSION_KEY] = user._meta.pk.value_to_string(user)
We can also see that Django takes the session key and casts it to the primary keys type using:
get_user_model()._meta.pk.to_python(request.session[SESSION_KEY])
I want to change the session key from the user's name to the id I am using in my backend.
However, if I change the primary key on my user model, I will break active sessions that are using the user name as the session key.
How might I change the user field without breaking active sessions?
Share Improve this question edited Nov 25, 2024 at 20:30 Baz asked Nov 21, 2024 at 22:10 BazBaz 13.1k40 gold badges151 silver badges278 bronze badges 6 | Show 1 more comment1 Answer
Reset to default 2 +250How does Django finds the user for a given session
The details are, as probably in most web frameworks, a bit messy, and requires some digging. The request.user
is set in the AuthenticationMiddleware
[Django-doc] with a SimpleLazyObject
[GitHub]:
class AuthenticationMiddleware(MiddlewareMixin): def process_request(self, request): if not hasattr(request, "session"): raise ImproperlyConfigured( "The Django authentication middleware requires session " "middleware to be installed. Edit your MIDDLEWARE setting to " "insert " "'django.contrib.sessions.middleware.SessionMiddleware' before " "'django.contrib.auth.middleware.AuthenticationMiddleware'." ) request.user = SimpleLazyObject(lambda: get_user(request)) request.auser = partial(auser, request)
The get_user(…)
[GitHub] itself does not much more than checking if the user is somehow already cached, and if not fetch it:
def get_user(request): if not hasattr(request, "_cached_user"): request._cached_user = auth.get_user(request) return request._cached_user
Now the interesting part is the auth.get_user(…)
[GitHub], which looks if the session data contains the user session key, and the backend to use:
def get_user(request): # … user_id = _get_user_session_key(request) backend_path = request.session[BACKEND_SESSION_KEY] # …
It is thus important to note that the session does not only keep the user_id
, but also the backend that was used for that id, to prevent having to enumerate over the backends again.
But even if that was not the case, it is not easy to just change the backend. Indeed, if we look at _get_user_session_key(…)
[GitHub], we see:
def _get_user_session_key(request): # This value in the session is always serialized to a string, so we need # to convert it back to Python whenever we access it. return get_user_model()._meta.pk.to_python(request.session[SESSION_KEY])
It thus uses the primary key field to "deserialize" the session key, even if you would have used a different field first.
This is the same when we log in: in the login(…)
function [GitHub], it sets the primary key of the user:
def login(request, user, backend=None): # … request.session[SESSION_KEY] = user._meta.pk.value_to_string(user) # …
The two are thus much tailored towards the primary key. The (default) ModelBackend
, does not do much, except fetching the item with the primary key [GitHub]:
def get_user(self, user_id): try: user = UserModel._default_manager.get(pk=user_id) except UserModel.DoesNotExist: return None return user if self.user_can_authenticate(user) else None
Work (temporary) with two primary keys
However, if I change the primary key on my user model to the id field, I will break active sessions that are using the user name as the session key.
You coud monkey-patch the .get_user(…)
method of the ModelBackend
, for example in the .ready()
method [Django-doc] of any of the AppConfig
s in your project:
# app_name/apps.py
from django.apps import AppConfig
class MyAppConfig(AppConfig):
def ready(self):
from django.contrib.auth import get_user_model
UserModel = get_user_model()
def get_user(self, user_id):
try:
user = UserModel._default_manager.get(pk=user_id)
except UserModel.DoesNotExist:
try:
user = UserModel._default_manager.get(username=user_id)
except UserModel.DoesNotExist:
return None
return user if self.user_can_authenticate(user) else None
from django.contrib.auth.backends import ModelBackend
ModelBackend.get_user = get_user # 🖘 monkey-patch
But that being said, I don't think that people getting logged out is that much of a big deal: eventually people will get logged out, because the cookie expires holding the session, or because of other reasons.
I think that using two natural keys imposes a security risk: if I somehow know the UUID of a certain user, I could try to rename the username to that UUID, essentially stealing the session.
Conclusion
What is perhaps more important is that the primary key of a model should typically be seen as a blackbox item. Sure, the primary key of many models is in fact an integer, but that is a technical detail. It makes not much sense to add two primary keys together for example. In database design, typically they teach to make the primary key a natural key. It is more a "Django-ism" to always use a black-box object, such as an AutoField
, or a UUIDField
.
While one can use monkey patch, it only introduces security risks, so I would advise not to do that, and let the sessions just expire: people then can login again. Changing the primary key imposes also the same risks.
本文标签: djangoMoving User pk to another fieldStack Overflow
版权声明:本文标题:django - Moving User pk to another field - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1736306922a1933132.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
.get_user(user_id)
from the backend, unless you use some sort of caching framework in which case it will use the._cached_user
of the request. It will use the backend specified in the session data under the_auth_user_backend
key. – willeM_ Van Onsem Commented Nov 21, 2024 at 23:37AutoField
and not their username which just has a unique constraint. – Abdul Aziz Barkat Commented Nov 24, 2024 at 16:53