弱密码校验是 Django 1.9 新功能之一,可以通过 settings 中的 AUTH_PASSWORD_VALIDATORS 来开启。 以下是四个自带的校验器,当然也可以自己写校验器,只需要按照下述格式添加进去就可以做统一校验。
# settings.py
AUTH_PASSWORD_VALIDATORS = [
{
# 用户属性相似验证,检查密码和一组用户的属性的相似性
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
},
{
# 最小长度验证,最小接受长度为 9
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
'OPTIONS': {
'min_length': 9,
}
},
{
# 常见密码验证,这个检查器会对比常用的弱密码,这些常用密码被 gzip 打包储存在 `django/contrib/auth/common-passwords.txt.gz` 中
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
},
{
# 纯数字密码验证
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
},
]
并不是直接 set_password 就会校验,而是需要在 set_password 前手动调用,用法如下
from django.contrib.auth.password_validation import validate_password
validate_password(password, user=None, password_validators=None)
其中 password 是 raw string 的 password 字符串 user 是检查的用户实例,非必须。如果 validator 需要根据 user 实例做判断,那么就需要传 * password_validators 是 list,包含所有需要校验的 validator,不指定的话默认校验所有 settings.py 中指定的 validator 如果所有 validator 都通过,返回 None;否则 raise 包含所有错误消息的 ValidationError。 实战:在 form 中校验用户的两次密码输入
class PasswordInitForm(forms.Form):
# RepeatCharField 是自定义的一个 MultiValueField
# 它接受两个 CharField 输入,并检查其是否一致
# 如果不一致,raise ValidationError;否则返回相同的 CharField 值
password = RepeatCharField()
def clean_password(self):
validate_password(self.cleaned_data['password'])
return self.cleaned_data['password']
我的 view
是这样的
class PasswordInitView(LoginRequiredMixin, TemplateView):
template_name = 'user/password_init.html'
@json_resp()
def post(self, request):
form = PasswordInitForm(request.POST)
result = {}
if form.is_valid():
request.user.set_password(form.cleaned_data['password'])
request.user.need_reset_password = False
request.user.save()
result['next'] = get_redirect_url(request, 'POST')
else:
result = Error(form.errors)
return result
附上 RepeatCharField
的代码
class RepeatCharWidget(MultiWidget):
def __init__(self):
widgets = (
TextInput(),
TextInput(),
)
super(RepeatCharWidget, self).__init__(widgets)
class RepeatCharField(MultiValueField):
def __init__(self, *args, **kwargs):
fields = (
CharField(),
CharField(),
)
if 'error_messages' not in kwargs or 'invalid' not in kwargs.get('error_messages'):
if 'error_messages' not in kwargs:
kwargs['error_messages'] = {}
kwargs['error_messages'].update({'invalid': ugettext_lazy('Invalid repeat input')})
kwargs['widget'] = kwargs.pop('widget', RepeatCharWidget())
super(RepeatCharField, self).__init__(fields, *args, **kwargs)
def compress(self, data_list):
return data_list[0]
def clean(self, value):
if value[0] != value[1]:
raise forms.ValidationError(getattr(self, 'error_messages', {}).get('invalid', ugettext_lazy('Repeat input should be the same')))
return super(RepeatCharField, self).clean(value)