Django:即使密码已保存为哈希,也将返回false
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Django:即使密码已保存为哈希,也将返回false相关的知识,希望对你有一定的参考价值。
我正在使用Django == 2.1.5
我的目标是基于DRF的简单创建用户和登录名,但我不想使用用户名(和pw)登录,而是要使用电子邮件地址+ pw登录。
因此我将USERNAME_FIELD ='email'保存在models.py中,实现了我自己的backends.py,在其中我使用了内部的check_password覆盖了身份验证。您可以在下面的代码中看到,我将密码作为纯文本传递(来自请求),但始终返回False->,因此authenticate返回None,并且我无法登录(views.py中的打印行< [print(“ login”,email,password,user_logged_in)例如打印* login bla@bla.com test123 None)。
用户创建正常;用户具有is_active = true和已编码的密码,例如“ argon2 $ argon2i $ v = 19 $ m = 512,t = 2,p = 2 $ bmJUV1RLWXVWRVdX $ mTJwkk / E / e2b8xNd2ZJkAw”。我想指出的另一件事是,在admin ui中,密码字段是可编辑的,并且与db(postgres)中的密码字段完全相同。据我所知,admin ui中的密码字段应该是只读的,并且有所不同-我不知道这是否对任何人都有用,但也许是有用的信息。对于迁移过程,我必须为密码添加一个默认值,因为它不能为空(password字段在AbstractBaseUser Model中)->因此有一个文件
../ migrations / 0001_initial.py
与行[[('password',models.CharField(max_length = 128,default = 1111)),settings.pyimport os
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/2.1/howto/deployment/checklist/
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = 'WONT TELL'
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True
ALLOWED_HOSTS = ['127.0.0.1']
# Application definition
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'django.contrib.sites',
'corsheaders',
'sslserver',
'posts',
'allauth',
'rest_framework',
'rest_auth',
'rest_framework_simplejwt.token_blacklist',
]
SITE_ID = 1
MIDDLEWARE = [
'corsheaders.middleware.CorsMiddleware',
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
AUTHENTICATION_BACKENDS = (
'walkAdog.backends.EmailAuthBackend',
'django.contrib.auth.backends.ModelBackend',
)
ROOT_URLCONF = 'walkAdog.urls'
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
WSGI_APPLICATION = 'walkAdog.wsgi.application'
# Database
# https://docs.djangoproject.com/en/2.1/ref/settings/#databases
#DATABASES = {
# 'default': {
# 'ENGINE': 'django.db.backends.sqlite3',
# 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
# }
#}
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql_psycopg2',
'NAME': 'wad_db',
'USER': 'Nini',
'PASSWORD': '',
'HOST': 'localhost',
'PORT': '5432',
}
}
# Password validation
# https://docs.djangoproject.com/en/2.1/ref/settings/#auth-password-validators
AUTH_PASSWORD_VALIDATORS = [
{
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
},
]
PASSWORD_HASHERS = [
'django.contrib.auth.hashers.Argon2PasswordHasher',
]
# Internationalization
# https://docs.djangoproject.com/en/2.1/topics/i18n/
LANGUAGE_CODE = 'en-us'
TIME_ZONE = 'UTC'
USE_I18N = True
USE_L10N = True
USE_TZ = True
# Static files (CSS, javascript, Images)
# https://docs.djangoproject.com/en/2.1/howto/static-files/
MEDIA_ROOT = os.path.join(BASE_DIR,'media')
STATIC_URL = '/static/'
MEDIA_URL = '/media/'
AUTH_USER_MODEL = 'posts.User'
#Added because of https://dev.to/callmetarush/the-django-rest-custom-user-model-and-authentication-5go9
ACCOUNT_USER_MODEL_USERNAME_FIELD = None
ACCOUNT_AUTHENTICATION_METHOD = 'email'
ACCOUNT_USERNAME_REQUIRED = False
ACCOUNT_EMAIL_REQUIRED = True
ACCOUNT_UNIQUE_EMAIL = True
ACCOUNT_USERNAME_REQUIRED = False
ACCOUNT_USER_EMAIL_FIELD = 'email'
ACCOUNT_LOGOUT_ON_GET = True
REST_AUTH_SERIALIZERS = {
"USER_DETAILS_SERIALIZER": "posts.serializers.UserSerializer",
}
REST_AUTH_REGISTER_SERIALIZERS = {
"REGISTER_SERIALIZER": "posts.serializers.NewUserSerializer",
}
#https://medium.com/@apogiatzis/create-a-restful-api-with-users-and-jwt-authentication-using-django-1-11-drf-part-2-eb6fdcf71f45
REST_FRAMEWORK = {
'DEFAULT_PERMISSION_CLASSES': (
'rest_framework.permissions.AllowAny',
#'rest_framework.permissions.DjangoModelPermissionsOrAnonReadOnly',
'rest_framework.permissions.IsAuthenticated',
),
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.BasicAuthentication',
'rest_framework_simplejwt.authentication.JWTAuthentication',
'rest_framework.authentication.SessionAuthentication',
),
'DEFAULT_PARSER_CLASSES': (
'rest_framework.parsers.JSONParser',
'rest_framework.parsers.FormParser',
)
}
#https://github.com/davesque/django-rest-framework-simplejwt
from datetime import timedelta
SIMPLE_JWT = {
'ACCESS_TOKEN_LIFETIME': timedelta(minutes=60),
'REFRESH_TOKEN_LIFETIME': timedelta(days=1),
'ROTATE_REFRESH_TOKENS': False,
'BLACKLIST_AFTER_ROTATION': True,
'ALGORITHM': 'HS256',
'SIGNING_KEY': SECRET_KEY,
'VERIFYING_KEY': None,
'AUTH_HEADER_TYPES': ('Bearer',),
'USER_ID_FIELD': 'id',
'USER_ID_CLAIM': 'user_id',
'AUTH_TOKEN_CLASSES': ('rest_framework_simplejwt.tokens.AccessToken',),
'TOKEN_TYPE_CLAIM': 'token_type',
'SLIDING_TOKEN_REFRESH_EXP_CLAIM': 'refresh_exp',
'SLIDING_TOKEN_LIFETIME': timedelta(minutes=5),
'SLIDING_TOKEN_REFRESH_LIFETIME': timedelta(days=3),
}
CORS_ORIGIN_WHITELIST = 'localhost:3000',
#production
#SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')
#SESSION_COOKIE_SECURE = True
#CSRF_COOKIE_SECURE = True
#SECURE_SSL_REDIRECT = True
#Local
#this setting tells Django to trust the X-Forwarded-Proto header coming from the proxy (Apache, nginx..).
SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')
SESSION_COOKIE_SECURE = True
CSRF_COOKIE_SECURE = True
SECURE_SSL_REDIRECT = True
models.py
# Create your models here
class UserAccountManager(BaseUserManager):
use_in_migrations = True
def create_user(self, username, email, password):
user = self.model(
email=self.normalize_email(email),
username=username)
user.set_password(password)
user.save(using=self._db)
return user
def create_superuser(self,username, email, password):
user = self.model(
email=self.normalize_email(email),
username=username,
)
user.set_password(password)
user.save(using=self._db)
user.is_admin=True
user.is_staff=True
user.save(using=self._db)
permission = Permission.objects.all()
for i in permission:
user.user_permissions.add(i)
return user
def get_by_natural_key(self, email):
return self.get(email=email)
def avatar_upload(instance, filename): #https://c14l.com/blog/django-image-upload-to-dynamic-path.html
#return os.path.join('images/user_avatar/', 'user_{0}', '{1}').format(instance.user.id, filename)
new_filename = '{}.{}'.format(instance.id, 'png')
return "images/user_avatar/{}".format(new_filename)
# related_name goes as plural and related_query_name goes as singular
class User(AbstractBaseUser,PermissionsMixin):
id = models.AutoField(primary_key=True) # custom User models must have an integer PK
username = models.CharField(('username'), max_length=30, blank=True, null=True,
help_text= ('30 characters or fewer. Letters, digits and _ only.'),
validators=[
validators.RegexValidator(
r'^w+$', ('Enter a valid username. This value may contain only '
'letters, numbers and _ character.'),
'invalid'
),
],
error_messages={
'unique': ("The username is already taken."),
}
)
email = models.EmailField(('Email address'), unique=True,
error_messages={
'unique': ("A user with that email already exists."),
}
)
is_staff = models.BooleanField(default=True) #Django Admin is using is_staff flag to authorize you
is_superuser = models.BooleanField(default=True)
is_admin = models.BooleanField(default=False)
is_active = models.BooleanField(default=True)
is_premium = models.BooleanField(default=False)
last_login = models.DateTimeField(blank=True, null=True, verbose_name='last_login')
GENDER_CHOICES = (
('Mann', 'Mann'),
('Frau', 'Frau'),
)
gender = models.CharField(choices=GENDER_CHOICES, max_length=4,default='Frau')
avatar = models.ImageField(upload_to=avatar_upload, default='default_l.png', validators=[validators.FileExtensionValidator(['jpg','jpeg', 'gif', 'png',])])
address = models.TextField(max_length=255, blank=True, null=True)
current_location = models.CharField(max_length=100, null=True)#The lon comes before lat.
location_x = models.CharField(max_length=100, null=True)
location_y = models.CharField(max_length=100, null=True)
oauth_token = models.CharField(max_length=255, blank=True, null=True)
#token_expiration = models.DateTimeField(blank=True, null=True, verbose_name='token_exp')
token = models.CharField(max_length=255, blank=True, null=True)
email_confirmed = models.BooleanField(default=False, blank=True)
friends = models.ManyToManyField("self", symmetrical=False, through='Freundschaft')
birthday = models.DateField(null=True, blank=True)
created_at = models.DateTimeField(auto_now_add=True)
# location
# online; unterwegs wenn im Walk, offline, invisbile
WALKSTATUS_LIST = (
('online', 'online'),
('unterwegs', 'unterwegs'),
('offline', 'offline'),
('invisbile', 'invisbile'),
)
walkstatus = CharField(max_length=9,
choices=WALKSTATUS_LIST,
default='online')
# leine = true, frei = false
leine = models.BooleanField(default=True)
walk_started = models.BigIntegerField(primary_key=False, blank=True, null=True) #the walk that was started (cause user is not only a member)
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ['username']
objects = UserAccountManager()
class Meta:
unique_together = ('username', 'email')
ordering = ['username']
def __str__(self):
return "%s %s %s" % (self.username, self.walkstatus, self.leine)
def natural_key(self):
return self.email
def get_full_name(self):
pass
def get_short_name(self):
pass
def has_perm(self, perm, obj=None):
return self.is_admin
def has_module_perms(self, app_label):
return self.is_admin
def get_absolute_url(self):
kwargs = {
'pk': self.id,
}
return reverse('posts:user_profile', kwargs=kwargs)
def get_by_natural_key(self, email):
# pylint: disable=E1101
return self.User.get(email=email)
# pylint: enable=E1101
我在views.py中的登录视图
class LoginView(views.APIView):
permission_classes = (AllowAny,)
def post(self, request, format='json'):
serializer = LoginSerializer
email = request.data['email']
password = request.data['password']
user_logged_in = EmailAuthBackend().authenticate(request=request,username=email,password=password)
#Note the use of the authenticate() function to check whether the username and password provided match to a valid user account, and the login() function to signify to Django that the user is to be logged in.
print ("login", email, password, user_logged_in)
if user_logged_in:
login(request, user_logged_in)
#profile active?
#if not user.email_confirmed:
# Redirect to a profile page and throw notification "please confirm email".
# return JsonResponse({'success': 'true', 'token': token, 'user': user, 'msg': 'We have sent you an email to confirm your email adress. Please confirm your email adress through the given link in the URL.'})
# Is the account active? It could have been disabled.
if not user_logged_in.is_active:
#send notification to frontend that reactivation happend
user_logged_in.is_active=True
return Response(serializer.data)
else:
return Response('Please provide valid email address and password.')
backends.py
from django.contrib.auth.backends import ModelBackend
from django.contrib.auth import get_user_model
from django.contrib.auth.hashers import check_password
# https://riptutorial.com/django/example/4212/email-authentication-backend
User = get_user_model()
from django.contrib.auth.hashers import make_password
class EmailAuthBackend(ModelBackend):
"""
Email Authentication Backend
Allows a user to sign in using an email/password pair rather than
a username/password pair.
"""
def authenticate(self, request, username, password, **kwargs):
""" Authenticate a user based on email address as the user name. """
print('inside custom auth')
try:
user = User.objects.get(email=username)
print('help', user.check_password(request.data['password']))
# Django Hasher is PBKDF2PasswordHasher (look at https://docs.djangoproject.com/en/3.0/topics/auth/passwords/)
if user.check_password(request.data['password']):
return user # return user to be authenticated
else:
return None
except Exception as e: print(e)
def get_user(self, user_id):
try:
return User.objects.get(pk=user_id)
except User.DoesNotExist:
return None
以上是关于Django:即使密码已保存为哈希,也将返回false的主要内容,如果未能解决你的问题,请参考以下文章
即使配置了 Allowed_Host = ['localhost','127.0.0.1'],Django Debug = False 也会返回错误请求
SharedPrefs 似乎已被擦除,即使我使用它们来自动登录