我将 djang0-allauth 与我的应用程序集成,但有些东西无法完全正常工作。
每次我尝试通过访问
http://127.0.0.1:8000/accounts/google/login/
并遵循 google 身份验证流程来登录/签名时,我最终都会被发送到 http://127.0.0.1:8000/accounts/social/signup/
,在那里我陷入了某种登录和登录的循环中。
我想我没有正确设置我的设置?或者也许我需要对adapters.py做一些事情
仅就上下文而言,我有一个自定义用户模型,它可能也会产生问题?
设置
"""
Django settings for mywebsite project.
Generated by 'django-admin startproject' using Django 3.2.5.
For more information on this file, see
https://docs.djangoproject.com/en/3.2/topics/settings/
For the full list of settings and their values, see
https://docs.djangoproject.com/en/3.2/ref/settings/
"""
from pathlib import Path
import os
import django_on_heroku
import django_heroku
import cloudinary
import cloudinary_storage
import dj_database_url
from decouple import config
import cloudinary.uploader
import cloudinary.api
# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent
# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/3.2/howto/deployment/checklist/
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = os.environ.get('DJANGO_SECRET_KEY', False)
#env docs https://apple.stackexchange.com/questions/356441/how-to-add-permanent-environment-variable-in-zsh
LOCAL = os.environ.get('ARE_WE_LOCAL', False)
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True
DEBUG_PROPAGATE_EXCEPTIONS = True
ALLOWED_HOSTS = ['localhost', '127.0.0.1', 'produceit.herokuapp.com', 'ateam.productions', 'miliu.co']
# Application definition
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.sites',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'mywebsite',
'urlshortener.apps.UrlshortenerConfig',
'action.apps.ActionConfig',
'file_upload.apps.FileUploadConfig',
'myusermodel.apps.MyusermodelConfig',
'django_extensions',
'bootstrap_modal_forms',
'widget_tweaks',
'cloudinary',
'cloudinary_storage',
'guardian',
'allauth',
'allauth.account',
'allauth.socialaccount',
'allauth.socialaccount.providers.google',
'whitenoise.runserver_nostatic', # new
# https://dev.to/mdrhmn/django-google-authentication-using-django-allauth-18f8
]
MIDDLEWARE = [
#'django.middleware.security.SecurityMiddleware',
'whitenoise.middleware.WhiteNoiseMiddleware',
'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',
]
ROOT_URLCONF = 'mywebsite.urls'
PROJECT_ROOT = os.path.abspath(os.path.dirname(__file__))
# Other settings...
TEMPLATE_DIRS = (
os.path.join(PROJECT_ROOT, "templates"),
)
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 = 'mywebsite.wsgi.application'
# Database
# https://docs.djangoproject.com/en/3.2/ref/settings/#databases
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': BASE_DIR / 'db.sqlite3',
}
}
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',
},
]
# Internationalization
# https://docs.djangoproject.com/en/3.2/topics/i18n/
LANGUAGE_CODE = 'en-us'
TIME_ZONE = 'UTC'
USE_I18N = True
USE_L10N = True
USE_TZ = True
# Static files (CSS, JavaScript, Images)
#https://learndjango.com/tutorials/django-static-files
STATIC_URL = '/static/'
STATICFILES_DIRS = (str(BASE_DIR.joinpath('static')),)
STATIC_ROOT = str(BASE_DIR.joinpath('staticfiles'))
STATICFILES_STORAGE = 'whitenoise.storage.CompressedManifestStaticFilesStorage' # new
# https://docs.djangoproject.com/en/3.2/howto/static-files/
#STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles')
#STATIC_URL = '/static/'
#STATICFILES_DIRS = [ os.path.join(BASE_DIR, "static"),]
#STATICFILES_STORAGE = 'whitenoise.storage.CompressedManifestStaticFilesStorage'
# Cloudinary stuff
CLOUDINARY_API_KEY=os.environ.get('CLOUDINARY_API_KEY')
CLOUDINARY_API_SECRET=os.environ.get('CLOUDINARY_API_SECRET')
CLOUDINARY_STORAGE = {
'CLOUD_NAME': 'ateamproductions',
'API_KEY': CLOUDINARY_API_KEY,
'API_SECRET': CLOUDINARY_API_SECRET,
}
cloudinary.config(
cloud_name="ateamproductions",
api_key=CLOUDINARY_API_KEY,
api_secret=CLOUDINARY_API_SECRET
)
DEFAULT_FILE_STORAGE = 'cloudinary_storage.storage.MediaCloudinaryStorage'
# Default primary key field type
# https://docs.djangoproject.com/en/3.2/ref/settings/#default-auto-field
# DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
django_heroku.settings(locals())
#django_on_heroku.settings(locals())
# SECURE_SSL_REDIRECT=True
# SESSION_COOKIE_SECURE=True
# CSRF_COOKIE_SECURE=True
# SECURE_HSTS_SECONDS = 31536000
# SECURE_HSTS_INCLUDE_SUBDOMAINS=True
# SECURE_HSTS_PRELOAD=True
# import django_heroku
# django_heroku.settings(locals())
AUTHENTICATION_BACKENDS = [
'django.contrib.auth.backends.ModelBackend',
'allauth.account.auth_backends.AuthenticationBackend',
'guardian.backends.ObjectPermissionBackend',
]
SITE_ID = 3
LOGIN_REDIRECT_URL = '/'
ACCOUNT_ADAPTER = 'myusermodel.adapter.MyAccountAdapter'
# Additional configuration settings
SOCIALACCOUNT_QUERY_EMAIL = True
ACCOUNT_LOGOUT_ON_GET = True
ACCOUNT_UNIQUE_EMAIL = True
ACCOUNT_EMAIL_REQUIRED = True
ACCOUNT_USER_MODEL_USERNAME_FIELD = None
ACCOUNT_USERNAME_REQUIRED = False
ACCOUNT_AUTHENTICATION_METHOD = 'email'
AUTH_USER_MODEL = 'myusermodel.CustomUser'
SOCIALACCOUNT_PROVIDERS = {
'google': {
'SCOPE': [
'profile',
'email',
],
'AUTH_PARAMS': {
'access_type': 'online',
}
}
}
#django
# Debugging in heroku live
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'formatters': {
'verbose': {
'format': ('%(asctime)s [%(process)d] [%(levelname)s] ' +
'pathname=%(pathname)s lineno=%(lineno)s ' +
'funcname=%(funcName)s %(message)s'),
'datefmt': '%Y-%m-%d %H:%M:%S'
},
'simple': {
'format': '%(levelname)s %(message)s'
}
},
'handlers': {
'null': {
'level': 'DEBUG',
'class': 'logging.NullHandler',
},
'console': {
'level': 'DEBUG',
'class': 'logging.StreamHandler',
'formatter': 'verbose'
}
},
'loggers': {
'testlogger': {
'handlers': ['console'],
'level': 'INFO',
}
}
}
COMPRESS_ENABLED = os.environ.get('COMPRESS_ENABLED', False)
SMS_BACKEND = 'sms.backends.dummy.SmsBackend'
TWILIO_ACCOUNT_SID = os.environ.get('TWILIO_ACCOUNT_SID', False)
TWILIO_AUTH_TOKEN = os.environ.get('TWILIO_AUTH_TOKEN', False)
TWILIO_PHONE = os.environ.get('TWILIO_PHONE', False)
自定义用户模型
from django.contrib.auth.base_user import BaseUserManager
from django.db import models
from django.contrib.auth.models import AbstractUser
from django.utils.translation import ugettext_lazy as _
class CustomUserManager(BaseUserManager):
"""
Custom user model manager where email is the unique identifiers
for authentication instead of usernames.
"""
def create_user(self, email, password, **extra_fields):
"""
Create and save a User with the given email and password.
"""
if not email:
raise ValueError(_('The Email must be set'))
email = self.normalize_email(email)
user = self.model(email=email, **extra_fields)
user.set_password(password)
user.save()
return user
def create_superuser(self, email, password, **extra_fields):
"""
Create and save a SuperUser with the given email and password.
"""
extra_fields.setdefault('is_staff', True)
extra_fields.setdefault('is_superuser', True)
extra_fields.setdefault('is_active', True)
if extra_fields.get('is_staff') is not True:
raise ValueError(_('Superuser must have is_staff=True.'))
if extra_fields.get('is_superuser') is not True:
raise ValueError(_('Superuser must have is_superuser=True.'))
return self.create_user(email, password, **extra_fields)
class CustomUser(AbstractUser):
username = None
email = models.EmailField(_('email address'), unique=True)
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = []
objects = CustomUserManager()
def __str__(self):
return self.email
编辑 该问题似乎仅限于当我尝试使用数据库中已存在但不是通过 allauth 流程创建的帐户登录时。
我在以下场景中遇到了同样的问题:
SocialAccount
模型中删除用户即可。accounts/social/signup
据我了解,在重新连接期间,
django-allauth
检测到已经有用户注册,然后要求我使用现有用户登录,但我无法使用现有用户登录,因为没有这样的选项可以登录in(我需要 GitHub 身份验证才能登录)
这是我使用
signals.py
想出的修复方法:
from allauth.account.utils import perform_login
from allauth.socialaccount.signals import pre_social_login
from django.dispatch import receiver
from allauth.socialaccount.models import SocialAccount
from django.contrib.auth.models import User
def link_to_existing_user(sender, request, sociallogin, **kwargs):
# Check if social login user matches an existing user's username
username = sociallogin.account.extra_data.get('login')
if username:
# Try to get an existing user by username
try:
user = User.objects.get(username=username)
# Check if this social account is already linked to this user
existing_social_account = SocialAccount.objects.filter(user=user, provider='github').exists()
if not existing_social_account:
sociallogin.connect(request, user)
# Optionally, directly log in the user
perform_login(request, user, email_verification='optional')
except User.DoesNotExist:
pass # No matching user found by username
receiver(pre_social_login)(link_to_existing_user)
在
perform_login
之后,它会重定向到设置中定义的页面:
LOGIN_REDIRECT_URL = "/interests"
这样,我们就绕过了现有用户重定向页面的问题。
当然,您需要在应用程序中启用信号。