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

omhiaaxx  于 8个月前  发布在  Go
关注(0)|答案(1)|浏览(128)

我目前正在构建一个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作为模板变量user传递到相应的视图中(我知道我不应该这样做,它只是临时的)。
以下是一些其他相关的traceback:

/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中的第942行:return int(value)在收到value='None'时抛出一个Exception。
现在让我发布我的应用程序中的所有相关代码(希望如此)。

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类不再有属性字段-我在www.example.com中覆盖了它们models.py因为我没有使用数据库。因此,我的ApiUser示例没有pk,因此SESSION_KEY属性设置为'None'。当Django想要进行身份验证时,会话的SESSION_KEY属性被传递给前面提到的to_python()函数,这反过来又引发了一个Exception。
我的问题是,我该如何解决这个问题?我尝试将www.example.com设置user.pk为始终为1,并在www.example.com的authenticate函数中手动将session[SESSION_KEY]设置为1models.py,这导致我没有收到异常,但写入会话(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

mm5n2pyu

mm5n2pyu1#

我在类似的情况下,看到这个错误。对我来说,解决方案是在创建user对象时为自定义User类的id属性提供一个值。
这允许默认的login()函数在为所需的主键属性提供值时工作。
我的后端现在看起来像这样:

class CustomBackend(BaseBackend):
    def get_user(self, user_id):
        user = [...] # API call to get user data

        return CTUser(
            id=user_id, # <- added this
            username=user['cmsUserId'],
            email=user['email'],
            first_name=user['firstName'],
            last_name=user['lastName'],
        )

    def authenticate(self, request, username=None, password=None, **kwargs):
        userid = [...] # API call to authenticate and get user id

        user = self.get_user(userid)
        user.logintoken = password

        return user

相关问题