Django-allauth社交账号在登录时连接到现有账号。

20

我有一个自定义用户模型,并且我正在使用django-allauth进行社交注册和登录。当一个已经使用电子邮件注册过的用户使用社交账号登录时,我尝试将现有用户连接到新的社交账号。我在这个链接中找到了解决方法。

def pre_social_login(self, request, sociallogin):
    user = sociallogin.account.user
    if user.id:
        return
    try:
        customer = Customer.objects.get(email=user.email)
    except Customer.DoesNotExist:
        pass
    else:
        perform_login(request, customer, 'none')

但是当我尝试通过社交账户登录时,出现了错误。

RelatedObjectDoesNotExist at /accounts/facebook/login/callback/
SocialAccount has no user.

任何帮助将不胜感激。

我也意识到这存在安全问题。但是我仍然想尝试一下。


你有阅读关于自定义用户模型和django-allauth的部分吗? http://django-allauth.readthedocs.org/en/latest/advanced.html#custom-user-models - petkostas
3个回答

31

我通过稍微修改适配器的代码使其正常工作。

adapter.py

from allauth.socialaccount.adapter import DefaultSocialAccountAdapter

class MySocialAccountAdapter(DefaultSocialAccountAdapter):
    def pre_social_login(self, request, sociallogin): 
        user = sociallogin.user
        if user.id:  
            return          
        try:
            customer = Customer.objects.get(email=user.email)  # if user exists, connect the account to the existing account and login
            sociallogin.state['process'] = 'connect'                
            perform_login(request, customer, 'none')
        except Customer.DoesNotExist:
            pass

如果要继承DefaultSocialAccountAdapter,我们需要在settings.py文件中指定SOCIALACCOUNT_ADAPTER = 'myapp.my_adapter.MySocialAccountAdapter'


客户类在哪里? - Saurabh Verma
1
Customer类只是一个例子。我使用Django的AbstractBaseUser创建了一个自定义用户模型。 - anupsabraham
1
好的,你能否解释一下你是在allauth代码中进行了这个更改还是创建了一个单独的类? - Saurabh Verma
4
这是来自django allauth发送的信号。您可以在此处查看文档。http://django-allauth.readthedocs.org/en/latest/signals.html#allauth-socialaccount 我在adapter.py中编写的自定义适配器中捕获了该事件。 - anupsabraham
1
我不习惯这种评论,但是...天哪,你真是个巫师。它像魔法一样运作。我想知道为什么它没有在官方文档中(信号文档对该主题并不清晰)。 - Paolo Stefan
显示剩余4条评论

13
我在这里找到了以下解决方案,它还检查电子邮件地址是否已经验证。 点击此处
from allauth.account.models import EmailAddress

def pre_social_login(self, request, sociallogin):

        # social account already exists, so this is just a login
        if sociallogin.is_existing:
            return

        # some social logins don't have an email address
        if not sociallogin.email_addresses:
            return

        # find the first verified email that we get from this sociallogin
        verified_email = None
        for email in sociallogin.email_addresses:
            if email.verified:
                verified_email = email
                break

        # no verified emails found, nothing more to do
        if not verified_email:
            return

        # check if given email address already exists as a verified email on
        # an existing user's account
        try:
            existing_email = EmailAddress.objects.get(email__iexact=email.email, verified=True)
        except EmailAddress.DoesNotExist:
            return

        # if it does, connect this new social login to the existing user
        sociallogin.connect(request, existing_email.user)

如果您想跳过验证步骤,我认为这个解决方案仍然更好一些:
def pre_social_login(self, request, sociallogin):

    user = sociallogin.user
    if user.id:
        return
    if not user.email:
        return

    try:
        user = User.objects.get(email=user.email)  # if user exists, connect the account to the existing account and login
        sociallogin.connect(request, user)
    except User.DoesNotExist:
        pass

在 pre_social_login 函数中,有没有可能获取来自 OAuth 提供程序请求的电子邮件和其他数据? - rammanoj
我认为它是 email__isexact=verified_email.email - rptmat57
什么是__isexact?它是exact还是iexact? - aehlke

5

这已经在最新的allauth中实现,您可以在模板中使用以下内容:

{% load socialaccount %}
<a href="{% provider_login_url "spotify" process="connect" %}">connect spotify account</a>

该URL如下:
/accounts/spotify/login/?process=connect

不需要内部修改。

1
当我发布问题时,连接功能已经存在。问题是我无法在登录后自动连接帐户。如果您想了解更多有关我遇到的问题,请查看https://github.com/pennersr/django-allauth/issues/418。这很好地涵盖了该问题并帮助我找到了解决方案。 - anupsabraham

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接