通过电子邮件关联用户

Associate user by email

我想覆盖管道以将用户的电子邮件与其仅处于活动状态的帐户相关联。但是我需要后端进行常规登录。 AUTHENTICATION_BACKENDS(django.contrib.auth.backends.AllowAllUsersModelBackend) 允许对所有用户进行身份验证,但我只想使用 Google 登录对某些具有 is_active 的用户进行身份验证。

settings.py

AUTHENTICATION_BACKENDS = ['social_core.backends.google.GoogleOAuth2','django.contrib.auth.backends.AllowAllUsersModelBackend',]
# Extends default user with additional fields 
AUTH_USER_MODEL = 'pages.Profile' 
SOCIAL_AUTH_USER_MODEL = 'pages.Profile' 

# social auth configs for google
SOCIAL_AUTH_GOOGLE_OAUTH2_KEY = config('GOOGLE_OAUTH2_KEY')
SOCIAL_AUTH_GOOGLE_OAUTH2_SECRET = config('GOOGLE_OAUTH2_SECRET')
SOCIAL_AUTH_GOOGLE_OAUTH2_SCOPE = ['https://www.googleapis.com/auth/calendar']
SOCIAL_AUTH_JSONFIELD_ENABLED = True
SOCIAL_AUTH_GOOGLE_OAUTH2_AUTH_EXTRA_ARGUMENTS = {'access_type': 'offline','approval_prompt':'force'}
SESSION_COOKIE_SAMESITE = None
SOCIAL_AUTH_PIPELINE = (
    'social_core.pipeline.social_auth.social_details',
    'social_core.pipeline.social_auth.social_uid',
    'social_core.pipeline.social_auth.auth_allowed',
    'social_core.pipeline.social_auth.social_user',
    'social_core.pipeline.user.get_username',
    'social_core.pipeline.social_auth.associate_by_email',  # <--- enable this one
    'social_core.pipeline.user.create_user',
    'social_core.pipeline.social_auth.associate_user',
    'social_core.pipeline.social_auth.load_extra_data',
    'social_core.pipeline.user.user_details',
    'pages.pipeline.save_token'
) 

views.py

def login(request):
    if request.method == 'POST':
        form = AuthenticationForm(request.POST)
        username = request.POST['username']
        password = request.POST['password']
        user = authenticate(username=username, password=password)
        if user:
            if user.is_active:
                auth_login(request, user)
                return redirect('home')
            else:
                messages.error(request,'User blocked',extra_tags='login')
                return redirect('login')
        else:
            messages.error(request,'username or password not correct',extra_tags='login')
            return redirect('login')
    else:
        form = AuthenticationForm()
    return render(request, 'registration/login.html',{'form':form})

要覆盖原来的部分,

在你的pipeline.py(创建一个,如果你的应用程序目录中没有),定义一个方法:

from social_core.pipeline.partial import partial
from social_core.exceptions import AuthAlreadyAssociated, AuthException, AuthForbidden


@partial
def associate_by_email(backend, details, user=None, *args, **kwargs):
    # No user 
    if user:
        return None

    email = details.get('email')
    if email:
        # Try to associate accounts registered with the same email address,
        # only if it's a single object. AuthException is raised if multiple
        # objects are returned.
        users = list(backend.strategy.storage.user.get_users_by_email(email))

        #That's the line you want to add
        active_users = [user for user in users if user.is_active]

        if len(active_users) == 0:
            return None
        elif len(active_users) > 1:
                raise AuthException(
                    backend,
                    'The given email address is associated with another account'
                )
        else:
            return {'user': active_users[0],
                    'is_new': False}

然后在你的settings.py中替换行

SOCIAL_AUTH_PIPELINE = (
    ...
    'social_core.pipeline.social_auth.associate_by_email',  # <--- enable this
    ...
)

与:

SOCIAL_AUTH_PIPELINE = (
    ...
    'your_app.pipeline.associate_by_email',  # <--- enable this one
    ...
)

确保将管道部分放置在原来的位置。 原始方法不执行检查,因此要添加此行为,您必须编写自己的部分来覆盖此行为。 假设您有一个 Django 应用程序(为了简单起见,此处称为 your_app - 只需相应地更新名称即可。 部分 associate_by_email 的原始代码可以在 here.

找到

如果要将此检查添加到简单身份验证,可以在文件中提供自己的后端 your_app/backends.py:

from django.core.exceptions import ObjectDoesNotExist
from settings import settings
from django.contrib.auth.models import User
from django.contrib.auth.backends import ModelBackend

class LocalActiveBackend(object):
    def authenticate(self, username=None, password=None):
        try:
            # Add extra check for is_active
            user = User.objects.get(username=username, is_active=True)
        except User.DoesNotExist:
            return None

        pwd_valid = user.check_password(password)
    
        if pwd_valid:
            return user

        return None

    def get_user(self, user_id):
        try:
            return User.objects.get(pk=user_id)
        except User.DoesNotExist:
            return None

然后将该新后端附加到可用后端列表中:

AUTHENTICATION_BACKENDS = (
    'django.contrib.auth.backends.ModelBackend', # default

    ...

    'your_app.backends.LocalActiveBackend',
)

这将允许您在需要时通过密码和用户名进行简单的身份验证。