Django:即使密码另存为哈希,check_password返回false

时间:2019-12-30 13:45:55

标签: django authentication

我正在使用Django == 2.1.5

我的目标是基于DRF的简单创建用户和登录名,但我不想使用用户名(和pw)登录,而是要使用电子邮件地址+ pw登录。

因此,我将USERNAME_FIELD ='email'保存在models.py中,实现了自己的backends.py,在其中我使用check_password覆盖了身份验证。 正如您在下面的代码中看到的那样,我以纯文本形式(从请求中)传递密码,但是它始终返回False->所以authenticate返回None并且我无法登录(视图中的打印行) .py print(“登录名”,电子邮件,密码,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.py

import 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

1 个答案:

答案 0 :(得分:0)

..令人尴尬...我忘记正确设置密码(user.set_password(..))。 经过大量的搜索,尝试和重写,重要的行在创建视图中丢失了

def create(self, validated_data):
        user = User.objects.create_user(validated_data['username'],
             validated_data['email'], validated_data['password'])
        user.set_password(validated_data['password'])

此问题可以解决!