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
答案
None

以上是关于Django:即使密码已保存为哈希,也将返回false的主要内容,如果未能解决你的问题,请参考以下文章

Django-检查两个密码哈希是否具有相同的原始密码

哈希算法的应用举例

即使配置了 Allowed_Host = ['localhost','127.0.0.1'],Django Debug = False 也会返回错误请求

SharedPrefs 似乎已被擦除,即使我使用它们来自动登录

来自 PHP 的 crypt() 的 MD5 哈希密码是不是可移植到 Django 密码字段?

Django - 插入而不返回已保存对象的 ID