django - 无法登录,因为 Django 以纯文本格式保存密码,但是
问题描述
class UserManager(BaseUserManager):
def create_user(self, username, email, password=None):
if username is None:
raise TypeError('User should have a username')
if email is None:
raise TypeError('User should have an email')
user = self.model(
username=username,
email=self.normalize_email(email)
)
user.set_password(password)
user.save(using=self._db)
return user
def create_superuser(self, username, email, password=None):
if password is None:
raise TypeError('Password should not be none')
user = self.create_user(username, email, password)
user.is_superuser = True
user.is_staff = True
user.save()
return user
class User(AbstractBaseUser, PermissionsMixin):
username = models.CharField(
max_length = 255,
unique = True,
db_index = True
)
email = models.EmailField(
max_length = 255,
unique = True,
db_index = True
)
is_verified = models.BooleanField(default = False)
is_staff = models.BooleanField(default = True)
is_active = models.BooleanField(default = True)
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ['username']
objects = UserManager()
def __str__(self):
return self.email
def tokens(self):
refresh = RefreshToken.for_user(self)
return {
'refresh': str(refresh),
'access': str(refresh.access_token)
}
class RegisterSerializer(serializers.ModelSerializer):
password = serializers.CharField(
max_length = 255,
min_length = 6,
write_only = True
)
class Meta:
model = User
fields = ['email', 'username', 'password']
extra_kwargs = {'password': {'write_only': True, 'min_length': 5}}
def validate(self, attrs):
email = attrs.get('email', '')
username = attrs.get('username', '')
if not username.isalnum():
raise serializers.ValidationError(
"Username should contain only alphanumeric characters"
)
return attrs
def create(self, validated_data):
return User.objects.create_user(**validated_data)
class RegisterView(generics.GenericAPIView):
permission_classes = (AllowAny,)
serializer_class = RegisterSerializer
def post(self, request):
serializer = self.serializer_class(data=request.data)
serializer.is_valid(raise_exception=True)
serializer.save()
user_data = serializer.data
return Response(
serializer.data,
status=status.HTTP_201_CREATED
)
目前我正在使用django 3.1.3
and djangorestframework 3.12.2
。虽然我能够使用超级用户登录并正确获取令牌,但无法使用员工用户登录。从 Django admin 我看到只有超级用户有散列密码,所有其他用户都有纯文本密码。
解决方案
您不使用 设置密码user.password
,这只是模型上的一个文本字段,并且可以像其他任何字段一样工作。您必须:
- 首选:致电
user.set_password(value)
- 备份
make_password
:在保存之前手动计算密码
你已经有了一个create
方法,所以我们将首先使用它。
from django.contrib.auth.hashers import make_password
def create(self, validated_data):
pwd = validated_data.pop("password")
user = User.objects.create(**attrs)
user.set_password(pwd)
user.save(updated_fields=["password"])
return user
# or you could replace it in validated_data
def create(self, validated_data):
validated_data["password"] = make_password(validated_data["password"])
return User.objects.create(**attrs)
您有一个验证例程,但它是全局的。如果您将其设置为特定于密码字段,那么您可以在那里计算它而无需create
理会。人们通常不会这样做,因为“确认密码”字段也是标准的。
def validate_password(self, value):
# ...
return make_password(value)
现在,如果您想添加一个confirm_password
字段,那么无论如何您都需要validate(self, attrs)
比较这两个字段,所以我建议不要使用最后一种方法。