将django密码验证器与django rest framework validate_password集成

时间:2016-04-04 23:51:55

标签: django validation django-rest-framework

我试图将django validators 1.9与django rest框架序列化器集成。但序列化的用户' (django rest framework)与django验证器不兼容。

这是serializers.py

import django.contrib.auth.password_validation as validators
from rest_framework import serializers

    class RegisterUserSerializer(serializers.ModelSerializer):

        password = serializers.CharField(style={'input_type': 'password'}, write_only=True)

        class Meta:
            model = User
            fields = ('id', 'username', 'email, 'password')

        def validate_password(self, data):
            validators.validate_password(password=data, user=User)
            return data

        def create(self, validated_data):
            user = User.objects.create_user(**validated_data)
            user.is_active = False
            user.save()
            return user

我设法将MinimumLengthValidator和NumericPasswordValidator设置为正确,因为两个函数验证都没有使用' user'在验证。源代码为here

摘自django源代码:

def validate(self, password, user=None):
        if password.isdigit():
            raise ValidationError(
                _("This password is entirely numeric."),
                code='password_entirely_numeric',
            )

对于像UserAttributeSimilarityValidator这样的其他验证器,该函数使用另一个参数' user'在验证中('用户'是django用户模型,如果我没有错)

摘自django源代码:

 def validate(self, password, user=None):
        if not user:
            return

        for attribute_name in self.user_attributes:
            value = getattr(user, attribute_name, None)

如何将序列化用户更改为django验证器(UserAttributeSimilarityValidator)可以看到的内容

摘自django源代码:

def validate(self, password, user=None):
        if not user:
            return

        for attribute_name in self.user_attributes:
            value = getattr(user, attribute_name, None)
            if not value or not isinstance(value, string_types):
                continue

修改

Django Rest Framework可以获得所有Django的内置密码验证(但它就像黑客一样)。这是一个问题:

validationError就像这样

  

[ValidationError(['此密码太短。必须包含at   至少8个字符。']),ValidationError(['此密码完全是   。数字'])]

验证不包含字段。 Django休息框架将其视为

{
    "non_field_errors": [
        "This password is too short. It must contain at least 8 characters.",
        "This password is entirely numeric."
    ]
}

如何在raise ValidationError

注入一个字段

4 个答案:

答案 0 :(得分:15)

与您提到的一样,当您使用password验证程序验证validate_password方法中的UserAttributeSimilarityValidator时,您没有user对象。

我建议您不要进行字段级验证,而应通过在序列化程序上实现validate方法来执行object-level validation

import sys
from django.core import exceptions
import django.contrib.auth.password_validation as validators

class RegisterUserSerializer(serializers.ModelSerializer):

     # rest of the code

     def validate(self, data):
         # here data has all the fields which have validated values
         # so we can create a User instance out of it
         user = User(**data)

         # get the password from the data
         password = data.get('password')

         errors = dict() 
         try:
             # validate the password and catch the exception
             validators.validate_password(password=password, user=User)

         # the exception raised here is different than serializers.ValidationError
         except exceptions.ValidationError as e:
             errors['password'] = list(e.messages)

         if errors:
             raise serializers.ValidationError(errors)

         return super(RegisterUserSerializer, self).validate(data)

答案 1 :(得分:9)

即使在进行字段级验证时,您也可以通过序列化程序对象上的self.instance访问用户对象。这样的事情应该有效:

 from django.contrib.auth import password_validation

 def validate_password(self, value):
    password_validation.validate_password(value, self.instance)
    return value

答案 2 :(得分:3)

使用序列化程序!有一个validate_fieldname方法!

class UserSerializer(serializers.ModelSerializer):

    class Meta:
        model = User
        fields = (
            'id', 'username', 'password', 'first_name', 'last_name', 'email'
        )
        extra_kwargs = {
            'password': {'write_only': True},
            'username': {'read_only': True}
        }

    def validate_password(self, value):
        try:
            validate_password(value)
        except ValidationError as exc:
            raise serializers.ValidationError(str(exc))
        return value

    def create(self, validated_data):
        user = super().create(validated_data)
        user.set_password(validated_data['password'])

        user.is_active = False
        user.save()
        return user

    def update(self, instance, validated_data):
        user = super().update(instance, validated_data)
        if 'password' in validated_data:
            user.set_password(validated_data['password'])
            user.save()
        return user

答案 3 :(得分:0)

在创建新用户(注册)时,self.instance将为none,它将在何时工作 您正在使用密码更改密码,更改密码或更新用户数据。 但是如果你想检查密码不应该与你的电子邮件或用户名类似,那么你需要包括" SequenceMatcher" 在您的验证中

data = self.get_initial()
username = data.get("username")
email = data.get("email")
password = data.get("password") 
max_similarity = 0.7
if SequenceMatcher(a=password.lower(), b=username.lower()).quick_ratio() > max_similarity:
    raise serializers.ValidationError("The password is too similar to the username.")
if SequenceMatcher(a=password.lower(), b=email.lower()).quick_ratio() > max_similarity:
    raise serializers.ValidationError("The password is too similar to the email.")