本文介绍了通过UserCreateForm绕过UserManager,通过UserCreateForm创建的普通用户可以进行身份​​验证,但不能在Shell中创建的超级用户吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个自定义用户模型和一个定义如下的用户管理器:

I have a custom user model and a user manager defined as follows:

/accounts/models.py

from django.contrib.auth.models import (
    AbstractBaseUser,
    BaseUserManager,
    PermissionsMixin
)
from django.db import models
from django.utils import timezone


class UserManager(BaseUserManager):
    def create_user(self, email, first_name, last_name, username=None, password=None):
        if not email:
            raise ValueError("Users must have a valid email address")

        if not first_name and last_name:
            raise ValueError("Users must have a first and last name")

        created_username = ''.join([first_name.lower(), last_name[:1].lower()])
        i=2
        while User.objects.filter(username=created_username).exists():
            created_username = ''.join([first_name.lower(), last_name[:i].lower()])
            i+=1

        user = self.model(
            email=self.normalize_email(email),
            first_name=first_name,
            last_name=last_name,
            username=created_username
        )

        user.set_password(password)
        user.save()

        return user

    def create_superuser(self, email, first_name, last_name, password):
        user = self.create_user(
            email,
            first_name,
            last_name,
            password
        )

        user.is_staff = True
        user.is_admin = True
        user.is_superuser = True

        user.save()
        return user


class User(AbstractBaseUser, PermissionsMixin):
    email = models.EmailField(unique=True)
    first_name = models.CharField(max_length=40, blank=True)
    last_name = models.CharField(max_length=40, blank=True)
    username = models.CharField(max_length=40, unique=True, blank=True, editable=False)
    # display_name = models.CharField(max_length=150)
    bio = models.TextField(blank=True, null=True)
    avatar = models.ImageField(blank=True, null=True)

    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    is_active = models.BooleanField(default=True)
    is_staff = models.BooleanField(default=False)
    is_admin = models.BooleanField(default=False)

    objects = UserManager()

    USERNAME_FIELD = 'email'
    REQUIRED_FIELDS = ['first_name','last_name']

    def __str__(self):
        return "{} @{}".format(self.email, self.username)

    def get_short_name(self):
        return self.first_name

    def get_full_name(self):
        return ' '.join([self.first_name, self.last_name])

当从shell中注册超级用户时,这似乎完美地工作了.我有一个表单和一个视图,用于在我的网站上注册常规用户,如下所示:

This seems to work perfectly when registering a superuser from the shell. I have a form and a view set up to register regular users on my site as follows:

/accounts/forms.py

from django import forms
from django.contrib.auth import get_user_model
from django.contrib.auth.forms import UserCreationForm
from django.core.exceptions import ValidationError
from django.utils.translation import ugettext_lazy as _

auth_code = 'hamburger'

def validate_authorization(value):
    if value != auth_code:
        raise ValidationError(
            _('Must have valid authorization code in order to register.')
        )


class UserCreateForm(UserCreationForm):
    authorization_code = forms.CharField(max_length=10, required=True, validators=[validate_authorization])

    class Meta:
        model = get_user_model()
        fields = ("email", "first_name", "last_name", "password1", "password2", "authorization_code")

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.fields["email"].label = "Email Address"
        self.fields["first_name"].label = "First Name"
        self.fields["last_name"].label = "Last Name"
        self.fields["password1"].label = "Password"
        self.fields["password2"].label = "Password Confirmation"
        self.fields["authorization_code"].label = "Authorization Code"

/accounts/views.py

from django.shortcuts import render

from django.template import RequestContext
from django.contrib.auth import login, logout
from django.contrib.auth.forms import AuthenticationForm, UserCreationForm
from django.core.urlresolvers import reverse_lazy
from django.views import generic
from django.http import HttpResponseRedirect

from django.contrib.auth import get_user_model

from . import forms


class SigninView(generic.FormView):
    form_class = AuthenticationForm
    success_url = '/dashboard/' #reverse_lazy('index')
    template_name = 'accounts/signin.html'

    def get_form(self, form_class=None):
        if form_class is None:
            form_class = self.get_form_class()
        return form_class(self.request, **self.get_form_kwargs())

    def form_valid(self, form):
        login(self.request, form.get_user())
        return super().form_valid(form)


class SignoutView(generic.RedirectView):
    url = '/' #reverse_lazy("home")

    def get(self, request, *args, **kwargs):
        logout(request)
        return super().get(request, *args, **kwargs)


class RegisterView(generic.CreateView):
    form_class = forms.UserCreateForm
    success_url = '/'
    template_name = 'accounts/register.html'

    def form_valid(self, form):
        self.object = form.save(commit=False)
        form.instance.username = ''.join([form.instance.first_name.lower(), form.instance.last_name[:1].lower()])
        i=2
        while get_user_model().objects.filter(username=form.instance.username).exists():
            form.instance.username = ''.join([form.instance.first_name.lower(), form.instance.last_name[:i].lower()])
            i+=1
        form.save()
        return HttpResponseRedirect(self.get_success_url())
        # return super(RegisterView, self).form_valid(form)

我不知道为什么我的超级用户无法登录该网站,但我的普通用户可以登录.您还会注意到我有一个while语句,该语句根据输入的名字和姓氏自动生成用户名.最初,我仅在UserManager中拥有此功能,但是表单绕过了用户管理器,因此我必须在视图中添加相同的代码块.因此,从窗体创建的用户与从外壳程序(UserManager)创建的用户之间似乎脱节了.

I am at a loss as to why my superuser cannot log into the website but my regular users can. Also you will notice I have a while statement that auto generates a username based on the entered first and last name. Initially I had this only in the UserManager however, the form was bypassing the user manager and so I had to add the same block of code to my view. So there seems to be a disconnect between users created from the form versus users created from the shell (UserManager).

authorization_code 到位,因为我不希望任何人都可以在我的网站上注册,而且我不知道更好的方法.我愿意接受更好的建议.

The authorization_code is in place because I don't want just anybody to be able to register on my site and I didn't know a better way. I am open to better suggestions.

可能会有所帮助的其他信息

Additional information that may be helpful

settings.py

# Set user authentication model
AUTH_USER_MODEL = 'accounts.User'

Python 3.5,Django 1.10

Python 3.5, Django 1.10

在此先感谢您提供任何建议或见识.

Thank you in advance for any advice or insight.

推荐答案

问题已解决.

def create_superuser(self, email, first_name, last_name, password):
    user = self.create_user(
        email,
        first_name,
        last_name,
        password
    )

我忘记设置 password = password .通过查看数据库中的密码字段,似乎这也导致了(据我所知)绕过了< algorithm> $< iterations> $< salt> (每个Django docs > https://docs.djangoproject.com/en/1.10/topics/auth/passwords/),尽管密码仍以某种方式散列(而不是以纯文本形式存储),但超级用户的密码字段比普通用户的密码字段短得多.无论执行什么操作,它都不会存储实际密码,并且在尝试使用超级用户帐户登录时给我一个无效的用户名/密码.

I was forgetting to set password=password,. From looking at the password field in the database, it seems this was also resulting in (as close as I can tell) bypassing <algorithm>$<iterations>$<salt> (per the Django docs https://docs.djangoproject.com/en/1.10/topics/auth/passwords/) though the password was still being hashed in some way (not being stored in plain text) the password field for superusers was considerably shorter than the password field for normal users. Whatever it was doing, it was not storing the actual password and was giving me an invalid username/password when attempting to log in with a superuser account.

所以正确的方法是

def create_superuser(self, email, first_name, last_name, password):
    user = self.create_user(
        email,
        first_name,
        last_name,
        password=password,
    )

当从 AuthenticationForm 中保存用户时,我仍然不明白为什么在 UserManager 中绕过了 created_username 的原因,但是我找到了一种解决方法通过向视图添加相同的while语句.至少现在所有功能都可以使用.我仍然很想知道是否有人对此事有进一步的了解.

I still don't understand why created_username is being bypassed in the UserManager when saving a user from the AuthenticationForm but I found a workaround by adding the same while statement to the view. At least all is functional now. I'm still interested to learn if anybody has further insight into this matter.

这篇关于通过UserCreateForm绕过UserManager,通过UserCreateForm创建的普通用户可以进行身份​​验证,但不能在Shell中创建的超级用户吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

10-28 18:45