13

概括

如何在 Django + MongoEngine 中使用自定义用户模型自定义身份验证后端(以允许电子邮件/密码身份验证)? (是否需要自定义后端?......即,在使用 MongoEngine 进行身份验证时使用电子邮件作为用户名。)

在 Django 中进行身份验证时,在使用Mongo作为主数据存储的同时使用自定义用户对象的直接(且完整!)示例是否有任何文档?(Postgres 有更清晰、更全面的文档……)


细节

MongoEngine 似乎只给你两种身份验证方式——“经典”(又名“ mongoengine.django.auth.MongoEngineBackend ”)方式......或者......“自定义用户模型”(又名“ django.contrib.auth ”) .backends.ModelBackend ') 方式——在 Nicolas Cortot 对这里另一个问题的回答中或多或少地简洁地概述了这两种方式:

Python-Social-Auth failed with mongoEngine (Django)

这两种身份验证技术都使您可以访问类似于 Django 的 AbstractBaseUser 类的authenticate()方法——该方法依赖于check_password函数。但是,在您使用所谓的“自定义用户模型”身份验证风格(如上面的链接中所述)的那一刻......然后将其与自定义后端配对(以便使用电子邮件作为用户名)......你由于无法访问典型的 authenticate() 函数而遇到麻烦。

比如像这样...

account.models.py


# ...使用 postgres,我将继承 AbstractBaseUser...但使用 Mongo...(?)

从 django.conf 导入设置
从 mongoengine.fields 导入 EmailField, BooleanField
从 mongoengine.django.auth 导入用户 类我的用户(用户): email = EmailField(max_length=254, unique=True) is_active = BooleanField(默认=真) is_admin = BooleanField(默认=假) USERNAME_FIELD = '电子邮件' REQUIRED_FIELDS = '' ...

my_custom_backend.py

# ...是否需要自定义后端才能使用电子邮件而不是用户名进行身份验证?

从 django.conf 导入设置
从 django.contrib.auth.models 导入 check_password
#from mongoengine.django.auth 导入 check_password
#from django.contrib.auth.hashers 导入 check_password
从模型导入 MyUser

    类 EmailAuthBackend(对象):

        def 身份验证(自我,电子邮件=无,密码=无):

# ...呃哦,因为我没有使用带有预先存在的 authenticate() 的常用后端之一
# 方法,没有可用的本机 check_password() 函数。意味着我必须散列
#密码等

所以,看起来,我有义务编写自己的 check_password 函数。为了获得通常在 PostgreSQL 身份验证中发现的AbstractBaseUser类固有的所有优点,我必须完全扩展我的自定义用户模型,这看起来很老套,而且不能很干。

我在这里完全糊涂了吗?...即,如果我想在使用 MongoEngine 时使用电子邮件而不是用户名进行身份验证,是否真的完全没有必要使用自定义后端?

我觉得我可能对 Django 在身份验证方面如何与 MongoEngine 一起工作,以及我在该过程中如何建模和调用自定义用户对象/我的 MongoEngine 用户对象的特定子类有一个基本的误解......

因为——就像现在一样——我在浏览器中收到“ 'AnonymousUser' 对象没有属性'backend' ”错误消息。我还注意到这个问题有时会因为意想不到的原因而存在——即:可能,authenticate() 方法需要一个散列密码,或者因为登录名(电子邮件)太长......?有关可能属于后一种情况的更多情况,请参阅:

Django 注册表单“AnonymousUser”对象没有属性“后端”


设置.py

INSTALLED_APPS = (
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.sites',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'django.contrib.admin',
    'mongoengine.django.mongo_auth',
    '帐户',
)

AUTHENTICATION_BACKENDS = (
    'mongoengine.django.auth.MongoEngineBackend',
    #'accounts.my_custom_backend.EmailAuthBackend',
    #'django.contrib.auth.backends.ModelBackend',
)

AUTH_USER_MODEL = 'mongo_auth.MongoUser'
MONGOENGINE_USER_DOCUMENT = 'accounts.models.User'

帐户.views.py

从 django.contrib.auth 导入登录为 django_login
从 my_custom_backend 导入 EmailAuthBackend
从表单导入 AuthenticationForm

def 登录(请求):

    表单 = AuthenticationForm(data=request.POST)
    如果 form.is_valid():
        尝试:
            后端 = EmailAuthBackend()
            用户 = backend.authenticate(email=request.POST['email'], password=request.POST['password'])
            django_login(请求,用户)
            返回重定向('/')
        除了不存在:
            return HttpResponse('用户不存在')
    别的:
        表单 = AuthenticationForm()

    return render_to_response('accounts/login.html',
       {“形式”:形式},
       context_instance=RequestContext(请求))
4

2 回答 2

5

好吧,看起来最好的做法是不要将 Django 的用户交给 Mongo 进行身份验证...通过 Twitter 获得了这个金块:

@blogblimp我的简短回答:尽量避免用 MongoDB 替换 Django 用户模型。你失去了 Django 的所有力量,失去了 MongoDB 的速度。说真的,用户与一切有关,而 MongoDB 不是关系型的。

——丹尼尔·罗伊·格林菲尔德 (@pydanny) 2014 年 1 月 20 日


所以:我将只使用 PostgreSQL 进行身份验证,使用 Mongo 进行其他对象。这意味着在 Django 设置中命名/连接到两个数据库。回想起来,我认为道德是:永远不要仅仅因为 Mongo 很酷而使用它。Mongo 在 Django 世界中仍然是二等公民。

于 2014-01-27T01:34:42.523 回答
2

也许我来晚了,但我可以使用 mongoengine User + django authenticate 来完成电子邮件身份验证的任务,这是我的工作方式:

from django.contrib.auth import authenticate, login as do_login, logout as do_logout

def login(request):

    data = extractDataFromPost(request)
    email = data["email"]
    password = data["password"]

    try: 
        user = User.objects.get(username=email)
        if user.check_password(password):
            user.backend = 'mongoengine.django.auth.MongoEngineBackend'
            user = authenticate(username=email, password=password)
            do_login(request, user)
            request.session.set_expiry(3600000) # 1 hour timeout
            return jsonResponse(serializeUser(user)) 
        else:
            result = {'error':True, 'message':'Invalid credentials'}
            return jsonResponse(result) 
    except User.DoesNotExist:
        result = {'error':True, 'message':'Invalid credentials'}
        return jsonResponse(result)
于 2014-03-18T19:45:08.673 回答