开发者

Django, request.user is always Anonymous User

I am using a custom authentication backend for Django (which runs off couchdb). I have a custom user model.

As part of the login, I am doing a request.user = user and saving the user id in session. However, on subsequent requests, I am not able to retrieve the request.user. It is always an AnonymousUser. I can, however, retrieve the user id from the session and can confirm that the session cookie is being set correctly.

What am I missing?

I do not want 开发者_JAVA技巧to use a relational db as I want to maintain all my user data in couchdb.

Edit: I have written a class which does not inherit from Django's auth User. It, however, has the username and email attributes. For this reason, my backend does not return a class which derives from auth User.


The request.user is set by the django.contrib.auth.middleware.AuthenticationMiddleware.

Check django/contrib/auth/middleware.py:

class LazyUser(object):
    def __get__(self, request, obj_type=None):
        if not hasattr(request, '_cached_user'):
            from django.contrib.auth import get_user
            request._cached_user = get_user(request)
        return request._cached_user

class AuthenticationMiddleware(object):
    def process_request(self, request):
        request.__class__.user = LazyUser()
        return None

Then look at the get_user function in django/contrib/auth/__init__.py:

def get_user(request):
    from django.contrib.auth.models import AnonymousUser
    try:
        user_id = request.session[SESSION_KEY]
        backend_path = request.session[BACKEND_SESSION_KEY]
        backend = load_backend(backend_path)
        user = backend.get_user(user_id) or AnonymousUser()
    except KeyError:
        user = AnonymousUser()
    return user

Your backend will need to implement the get_user function.


I too have custom authentication backend and always got AnonymousUser after successful authentication and login. I had the get_user method in my backend. What I was missing was that get_user must get the user by pk only, not by email or whatever your credentials in authenticate are:

class AccountAuthBackend(object):

@staticmethod
def authenticate(email=None, password=None):
    try:
        user = User.objects.get(email=email)
        if user.check_password(password):
            return user
    except User.DoesNotExist:
        return None

@staticmethod
def get_user(id_):
    try:
        return User.objects.get(pk=id_) # <-- tried to get by email here
    except User.DoesNotExist:
        return None

Its easy to miss this line in the docs:

The get_user method takes a user_id – which could be a username, database ID or whatever, but has to be the primary key of your User object – and returns a User object.

It so happened that email is not primary key in my schema. Hope this saves somebody some time.


You say you've written a custom authentication backend, but in fact what you seem to have written is a complete custom authentication app, which doesn't interface with Django's contrib.auth.

If you want to use a non-relational database for your authentication data, all you need to do is create a class that provides two methods: get_user(user_id) and authenticate(**credentials). See the documentation. Once you have authenticated a user, you simply call Django's normal login methods. There should be no reason to manually set request.user or put anything into the session.

Update after edit That has nothing to do with it. There's no requirement that the user class derives from auth.models.User. You still just need to define a get_user method that will return an instance of your user class.


Please elaborate. If you are using a custom user model (which is different from a custom user PROFILE model), then you are basically on your own and the django.contrib.auth framework can not help you with authentication. If you are writing your own authentication system and are not using django.contrib.auth, then you need to turn that off because it seem to be interfering with your system.


In case you are using an API (Django-rest-framework) and accessing a view using a get, post, etc. methods.

You can get a user by sending the Bearer/JWT token corresponding to that user.

Wrong

# prints Anonymous User

def printUser(request):
   print(request.user)

Correct

# using decorators 
# prints username of the user 

@api_view(['GET'])  # or ['POST'] .... according to the requirement
def printUser()
    print(request.user)


I had similar problem when I used custom authentication backend. I used field different than the primary key in the method get_user. It directly solved after using primary key which must be number (not str)

def get_user(self, user_id):
    try:
        return User.objects.get(pk=user_id) # <-- must be primary key and number
    except User.DoesNotExist:
        return None


After sending Token using Authorization header, the token will be gotten in dispatch function as bellow: '''

def dispatch(self, request, *args, **kwargs):

    self.args = args
    self.kwargs = kwargs
    request = self.initialize_request(request, *args, **kwargs)
    self.request = request
    self.headers = self.default_response_headers  # deprecate?

    try:
        self.initial(request, *args, **kwargs)

        # Get the appropriate handler method
        if request.method.lower() in self.http_method_names:
            handler = getattr(self, request.method.lower(),
                              self.http_method_not_allowed)
        else:
            handler = self.http_method_not_allowed

        response = handler(request, *args, **kwargs)

    except Exception as exc:
        response = self.handle_exception(exc)

    self.response = self.finalize_response(request, response, *args, **kwargs)
    return self.response

So you are using django_role_permission's HasRoleMixin, the dispatch method of this mixin will hide dispatch of the view. I think that the solution is to redefine the mixin of roles-permissions


user = authenticate(username=username, password=password) 
if user is not None:
   return render(request, 'home.html',{'user_id':user.id})


Added these in my view

from rest_framework.permissions import IsAuthenticated
from rest_framework.authentication import TokenAuthentication

authentication_classes = (TokenAuthentication,)
permission_classes = (IsAuthenticated,)

and started getting original user

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜