Django 1.7中的自定义密码验证

13

我正在尝试为create user和change password管理表单添加自定义密码验证。在Django文档中,我没有看到如何做到这一点的任何内容。我在Stack Overflow上找到了这篇文章:Enforcing password strength requirements with django.contrib.auth.views.password_change,其中给出了两个解决方案。我尝试了两种方法,但都没有起作用。

这是我目前在我的应用程序admin.py中拥有的内容:

def validate_password_strength(value):
    """Validates that a password is as least 10 characters long and has at least
    2 digits and 1 Upper case letter.
    """
    min_length = 10

    if len(value) < min_length:
        raise ValidationError(_('Password must be at least {0} characters '
                                'long.').format(min_length))

    # check for 2 digits
    if sum(c.isdigit() for c in value) < 2:
        raise ValidationError(_('Password must container at least 2 digits.'))

    # check for uppercase letter
    if not any(c.isupper() for c in value):
        raise ValidationError(_('Password must container at least 1 uppercase letter.'))

class MySetPasswordForm(SetPasswordForm):
    def __init__(self, *args, **kwargs):
        super(MySetPasswordForm, self).__init__(*args, **kwargs)
        self.fields['password1'].validators.append(validate_password_strength)

但是我想不通的是,怎样才能使用 MySetPasswordForm。

我尝试了几种不同的方法。首先,我做了这个:

class UserAdmin(UserAdmin):
    form = MySetPasswordForm

# Re-register UserAdmin
admin.site.unregister(User)
admin.site.register(User, UserAdmin)

我遇到了这个错误:

<class 'elex_apis.energy.webservice.admin.UserAdmin'>: (admin.E016) The value of 'form' must inherit from 'BaseModelForm'.

那么我将MySetPasswordForm更改为继承自BaseModelForm,然后就出现了以下错误:

__init__() missing 1 required positional argument: 'user'

所以我添加了用户参数,现在MySetPasswordForm看起来像这样:

class MySetPasswordForm(BaseModelForm):
    def __init__(self, user, *args, **kwargs):
        super(MySetPasswordForm, self).__init__(user, *args, **kwargs)
        self.fields['password1'].validators.append(validate_password_strength)

但我仍然得到了之前相同的错误。

我简直不敢相信添加密码验证器会这么难。这肯定是一个非常普遍的需求,所以显然我必须漏掉了一些简单的东西。请问有人能够在这里提供一些帮助吗?

2个回答

8
最简单的方法是继承原始的UserAdmin并仅覆盖change_password_form
例如:
from django.contrib.auth import models as auth_models
from django.contrib.auth import admin as auth_admin
from django.contrib.auth import forms as auth_forms
from django.core.exceptions import ValidationError


def validate_password_strength(value):
    """Validates that a password is as least 10 characters long and has at least
    2 digits and 1 Upper case letter.
    """
    min_length = 10

    if len(value) < min_length:
        raise ValidationError(_('Password must be at least {0} characters '
                                'long.').format(min_length))

    # check for 2 digits
    if sum(c.isdigit() for c in value) < 2:
        raise ValidationError(_('Password must container at least 2 digits.'))

    # check for uppercase letter
    if not any(c.isupper() for c in value):
        raise ValidationError(_('Password must container at least 1 uppercase letter.'))

    return value


class AdminPasswordChangeForm(auth_forms.AdminPasswordChangeForm):
    def clean_password1(self):
        return validate_password_strength(self.cleaned_data['password1'])


class UserCreationForm(auth_forms.UserCreationForm):
    def clean_password1(self):
        return validate_password_strength(self.cleaned_data['password1'])


class UserAdmin(auth_admin.UserAdmin):
    change_password_form = AdminPasswordChangeForm
    add_form = UserCreationForm


# Re-register UserAdmin
admin.site.unregister(auth_models.User)
admin.site.register(auth_models.User, UserAdmin)

这确实验证了密码,但是当输入有效密码并且管理员返回到上一页时,它会显示“未设置密码”。然而,当我查看数据库时,在auth_user中有一个密码。 - Larry Martell
1
@LarryMartell:哎呀...忘记加上返回值了,请使用新版本尝试 :) - Wolph
太好了!非常感谢!! - Larry Martell

0

另一种方法是创建一个validators.py文件,在其中创建基于object的自定义类,如果输入的密码失败,则引发ValidationError。更多信息在这里

import re

from django.core.exceptions import ValidationError
from django.utils.translation import ugettext as _

class NumberValidator(object):
    def validate(self, password, user=None):
        if not re.findall('\d', password):
            raise ValidationError(
               _("The password must contain at least 1 digit, 0-9."),
               code='password_no_number',
            )

    def get_help_text(self):
        return _(
           "Your password must contain at least 1 digit, 0-9."
        )

class UppercaseValidator(object):
    def validate(self, password, user=None):
        if not re.findall('[A-Z]', password):
            raise ValidationError(
                _("The password must contain at least 1 uppercase letter, A-Z."),
                code='password_no_upper',
            )

    def get_help_text(self):
        return _(
            "Your password must contain at least 1 uppercase letter, A-Z."
        )

使用正确的路径点更新AUTH_PASSWORD_VALIDATORS设置以使用您的验证器:

AUTH_PASSWORD_VALIDATORS = [
    {'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
        'OPTIONS': {
            'min_length': 12, }
     },
    {'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', },
    {'NAME': 'my.project.validators.NumberValidator',
        'OPTIONS': {
            'min_digits': 3, }},
    {'NAME': 'my.project.validators.UppercaseValidator', },        
]

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接