Django:ValidationError使用is_authenticated并在从外部源进行身份验证时注销

时间:2017-08-16 13:26:30

标签: python django

我目前正在构建一个Django应用程序,它从外部源(通过HTTP)获取所有数据。因此,我不想使用自己的数据库。我已根据此SO问题中所述的解决方案实现了我自己的用户和远程用户后端:Django users and authentication from external source。我使用签名的cookie来存储会话。

实施上述规定后,我能够毫无错误地登录。但是,在检查用户是否在模板中进行身份验证时(或尝试在视图中将其注销时),我收到错误:

ValidationError at /products/
["'None' value must be an integer."]

在模板中的行

{% if user.is_authenticated %}

其中request.user作为模板变量用户在相应的视图中传递(我知道我不应该这样做,它只是暂时的)。

以下是其他一些相关的追溯:

/home/ines/.virtualenvs/diet/lib/python3.6/site-packages/django/contrib/auth/middleware.py in <lambda>
request.user = SimpleLazyObject(lambda: get_user(request))
 ...
/home/ines/.virtualenvs/diet/lib/python3.6/site-packages/django/contrib/auth/middleware.py in get_user
request._cached_user = auth.get_user(request)
 ...
/home/ines/.virtualenvs/diet/lib/python3.6/site-packages/django/contrib/auth/__init__.py in get_user
user_id = _get_user_session_key(request)
 ...
/home/ines/.virtualenvs/diet/lib/python3.6/site-packages/django/contrib/auth/__init__.py in _get_user_session_key
return get_user_model()._meta.pk.to_python(request.session[SESSION_KEY])

错误发生在to_python()调用中,其中文件django / db / models / fields / __ init__.py:return int(value)中的行942在收到value='None'时抛出异常。

现在让我发布我的应用程序中的所有(希望)相关代码。

settings.py

AUTHENTICATION_BACKENDS = ('products.backends.ApiAuthBackend',)

产品/ backends.py

from django.contrib.auth.backends import RemoteUserBackend
from products.api import API
from products.models import ApiUser

class ApiAuthBackend(RemoteUserBackend):
    """
    Authenticate against the API.
    """

    create_unknown_user = False  # does not create a User object if it is not in the database

    def authenticate(self, request, username=None, password=None):
        api = API()
        access_token = api.login(username=username, password=password)
        if api.logged_in():
            user = ApiUser()
            user.username = username
            request.session['access_token'] = access_token
            return user
        return None

    def get_user(self, user_id):
        return ApiUser(username=user_id)

产品/ models.py

from django.contrib.auth.models import AbstractUser

class ApiUser(AbstractUser):
    def save(self, **kwargs):
        """
        Disables saving to database.
        """
        pass

    objects = None
    username = ''
    first_name = ''
    last_name = ''
    email = ''
    password = ''
    groups = ''
    user_permissions = ''
    is_staff = False
    is_active = True
    is_superuser = False
    last_login = None
    date_joined = None

    def get_group_permissions(self, obj=None):
        return []

    def get_all_permissions(self, obj=None):
        return []

    def has_perm(self, perm, obj=None):
        return []

    def has_perms(self, obj=None, **kwargs):
        return []

    def has_module_perms(self, app_label):
        return []

进一步检查django / contrib / auth / __ init__.py,第154行:

request.session[SESSION_KEY] = user._meta.pk.value_to_string(user)

问题似乎是request.session[SESSION_KEY],其中SESSION_KEY = '_auth_user_id'设置为'None'。这是由于ApiUser类不再具有属性字段这一事实 - 我已经在models.py中覆盖了它们,因为我没有使用数据库。因此,我的ApiUser实例没有pk,因此SESSION_KEY属性设置为'None'。当Django想要进行身份验证时,会话的SESSION_KEY属性会传递给前面提到的to_python()函数,而该函数又会引发异常。

我的问题是,我该如何解决这个问题?我尝试将user.pk设置为1并在models.py中的authenticate函数中将session[SESSION_KEY]手动设置为1,这导致我没有得到异常,但是其他数据写入会话(access_token)已经消失了。

提前感谢你的时间和答案,如果碰巧在某个地方找到解决方案,我非常抱歉(我做了很多搜索,但是还没找到它)。

更新(根据Bear Brown的要求):

这是我的API类代码,不确定它是否真正相关,因为它只是用于与外部数据库进行通信,但无论如何。

产品/ api.py

import requests
import json

class API:
    URL = "[...]" 
    KEY = "[...]"

    LOGIN = "authenticate/access-token"
    REGISTER = "authenticate/register"

    session = None
    debug = True

    def __init__(self, **kwargs):
        if len(kwargs) > 2 or not kwargs:
            return
        try:
            self.login(**kwargs)
        except requests.exceptions.ConnectionError:
            print("Could not connect to the database.")
        except requests.exceptions.RequestException:
            print("Could not retrieve data from the database.")

    def login(self, **kwargs):
        if self.session is not None:
            return
        assert 0 < len(kwargs) < 3
        access_token = ''
        if len(kwargs) == 2:
            username, password = kwargs['username'], kwargs['password']
            url = self.URL + self.LOGIN
            user_data = {
                'username': username,
                'password': password,
                'active-check': 0,
                'api_key':  self.KEY
            }
            r = requests.post(url, data=user_data)
            r.raise_for_status()
            response = r.json()
            if response['success'] is True:
                access_token = response['access_token']
        else:  # len(kwargs) == 1
            access_token = kwargs['access_token']

        if access_token:
            self.session = requests.Session()
            self.session.headers.update({'Authorization': 'Client-ID ' + access_token})
            if self.debug: print("Successfully logged in.")

        return access_token

    def logout(self):
        if self.session is None:
            return
        self.session.close()
        self.session = None

    def logged_in(self):
        return self.session is not None

这是一个指向pastebin上的完整错误回溯的链接(添加到已经很长的帖子似乎太长了): full error traceback on pastebin

0 个答案:

没有答案