Django allauth社交登录:使用注册的电子邮件自动链接社交网站配置文件

nev*_*ves 38 django django-1.5 django-allauth

我的目标是为我的Django站点的用户创建最简单的登录体验.我想象的是:

  1. 登录屏幕显示给用户
  2. 用户选择使用Facebook或Google登录
  3. 用户在外部站点输入密码
  4. 用户可以作为经过身份验证的用户与我的网站进行互动

好的,这部分很简单,只需安装django-allauth并进行配置即可.

但我也想提供与本地用户一起使用该网站的选项.它还有另一个步骤:

  1. 登录屏幕显示给用户
  2. 用户选择注册
  3. 用户输入凭据
  4. 网站发送验证邮件
  5. 用户点击电子邮件链接,可以作为经过身份验证的用户与我的网站进行互动

好的,默认身份验证和allauth都可以做到.但现在是百万美元的问题.

如果他们更改登录方式,我该如何自动关联他们的Google,FB和本地帐户?

看到他们登录的方式,我有他们的电子邮件地址.是否有可能使用django-allauth?我知道我可以通过用户干预来做到这一点.今天,默认行为是拒绝登录,说明电子邮件已经注册.

如果不可能只进行配置,我会接受答案,这个答案给出了我应该在allauth代码中进行哪些修改以支持此工作流程的方向.

有很多理由这样做.用户将忘记他们用于进行身份验证的方法,有时会使用Google,有时使用FB,有时还会使用本地用户帐户.我们已经有很多本地用户帐户和社交帐户将是一个新功能.我希望用户保持自己的身份.我设想可以询问用户好友列表,所以如果他们使用Google登录,我也想拥有他们的FB帐户.

这是一个业余爱好网站,没有很好的安全要求,所以请不要回答这不是一个明智的安全实施.

稍后,我将创建一个自定义用户模型,将电子邮件作为登录ID.但我会很满意答案,让我自动关联具有所需用户名的默认用户模型的帐户.

我正在使用Django == 1.5.4和django-allauth == 0.13.0

ssp*_*oss 29

注意(2018-10-23):我不再使用它了.太多的魔法发生了.相反,我能SOCIALACCOUNT_EMAIL_REQUIRED'facebook': { 'VERIFIED_EMAIL': False, ... }.因此,allauth将重定向社交注册表单上的社交登录以输入有效的电子邮件地址.如果已经注册,则首先登录时出现错误,然后连接该帐户.对我来说足够公平.


我正在尝试改进这种用例并提出以下解决方案:

from allauth.account.models import EmailAddress
from allauth.socialaccount.adapter import DefaultSocialAccountAdapter

class SocialAccountAdapter(DefaultSocialAccountAdapter):
    def pre_social_login(self, request, sociallogin):
        """
        Invoked just after a user successfully authenticates via a
        social provider, but before the login is actually processed
        (and before the pre_social_login signal is emitted).

        We're trying to solve different use cases:
        - social account already exists, just go on
        - social account has no email or email is unknown, just go on
        - social account's email exists, link social account to existing user
        """

        # Ignore existing social accounts, just do this stuff for new ones
        if sociallogin.is_existing:
            return

        # some social logins don't have an email address, e.g. facebook accounts
        # with mobile numbers only, but allauth takes care of this case so just
        # ignore it
        if 'email' not in sociallogin.account.extra_data:
            return

        # check if given email address already exists.
        # Note: __iexact is used to ignore cases
        try:
            email = sociallogin.account.extra_data['email'].lower()
            email_address = EmailAddress.objects.get(email__iexact=email)

        # if it does not, let allauth take care of this new social account
        except EmailAddress.DoesNotExist:
            return

        # if it does, connect this new social login to the existing user
        user = email_address.user
        sociallogin.connect(request, user)
Run Code Online (Sandbox Code Playgroud)

据我所知,它似乎运作良好.但非常欢迎投入和建议!

  • 这比接受的答案要好得多.我正在使用它并可能整合一些修改; 如果我想出任何好的改进,我会留下另一条评论. (5认同)

els*_*sar 28

您需要覆盖sociallogin适配器,特别pre_social_login是在使用社交提供程序进行身份验证之后调用的方法,但是在allauth处理此登录之前.

my_adapter.py,做这样的事情

from django.contrib.auth.models import User

from allauth.account.models import EmailAccount
from allauth.exceptions import ImmediateHttpResponse
from allauth.socialaccount.adapter import DefaultSocialAccountAdapter


class MyAdapter(DefaultSocialAccountAdapter):
    def pre_social_login(self, request, sociallogin):
        # This isn't tested, but should work
        try:
            user = User.objects.get(email=sociallogin.email)
            sociallogin.connect(request, user)
            # Create a response object
            raise ImmediateHttpResponse(response)
        except User.DoesNotExist:
            pass
Run Code Online (Sandbox Code Playgroud)

在您的设置中,将社交适配器更改为适配器

SOCIALACCOUNT_ADAPTER = 'myapp.my_adapter.MyAdapter`
Run Code Online (Sandbox Code Playgroud)

您应该能够以这种方式将多个社交帐户连接到一个用户.

  • `raise ImmediateHttpResponse(response)` 中的 `response` 应该来自哪里? (3认同)
  • 如果您尝试连接已保存的socialaccount,则会抛出断言错误.所以只需添加一个if条件检查,看看socialaccount是否有pk.如果有回归 (2认同)

Vic*_*rat 9

根据babus对此相关主题的评论,上面提出的答案引入了一个很大的安全漏洞,在allauth docs中有记录:

"从Facebook文件中不清楚该帐户是否经过验证意味着该电子邮件地址也已经过验证.例如,验证也可以通过电话或信用卡进行.要在保险箱上方面,默认是将Facebook的电子邮件地址视为未经验证."

这样说,我可以使用您的电子邮件ID在Facebook注册或在Facebook上将您的电子邮件更改为您的电子邮件并登录该网站以访问您的帐户.

因此,考虑到这一点,并建立在@sspross答案的基础上,我的方法是将用户重定向到登录页面,并通知她/他的副本,并邀请他使用她/他的其他帐户登录,并链接他们一旦他们登录.我承认这与原始问题不同,但在这样做时,没有引入安全漏洞.

因此,我的适配器看起来像:

from django.contrib.auth.models import User
from allauth.account.models import EmailAddress
from allauth.exceptions import ImmediateHttpResponse
from django.shortcuts import redirect
from django.contrib import messages
from allauth.socialaccount.adapter import DefaultSocialAccountAdapter

class MyAdapter(DefaultSocialAccountAdapter):
    def pre_social_login(self, request, sociallogin):
        """
        Invoked just after a user successfully authenticates via a
        social provider, but before the login is actually processed
        (and before the pre_social_login signal is emitted).

        We're trying to solve different use cases:
        - social account already exists, just go on
        - social account has no email or email is unknown, just go on
        - social account's email exists, link social account to existing user
        """

        # Ignore existing social accounts, just do this stuff for new ones
        if sociallogin.is_existing:
            return

        # some social logins don't have an email address, e.g. facebook accounts
        # with mobile numbers only, but allauth takes care of this case so just
        # ignore it
        if 'email' not in sociallogin.account.extra_data:
            return

        # check if given email address already exists.
        # Note: __iexact is used to ignore cases
        try:
            email = sociallogin.account.extra_data['email'].lower()
            email_address = EmailAddress.objects.get(email__iexact=email)

        # if it does not, let allauth take care of this new social account
        except EmailAddress.DoesNotExist:
            return

        # if it does, bounce back to the login page
        account = User.objects.get(email=email).socialaccount_set.first()
        messages.error(request, "A "+account.provider.capitalize()+" account already exists associated to "+email_address.email+". Log in with that instead, and connect your "+sociallogin.account.provider.capitalize()+" account through your profile page to link them together.")       
        raise ImmediateHttpResponse(redirect('/accounts/login'))
Run Code Online (Sandbox Code Playgroud)

  • http://stackoverflow.com/questions/14280535/is-it-possible-to-check-if-an-email-is-confirmed-on-facebook (4认同)
  • 如果你真的想“将社交帐户链接到现有用户”,你必须用“sociallogin.connect(request, email_address.user)”替换“如果是,退回到登录页面”部分 (2认同)
  • 我已经读过,如果 FB 确实返回了一封电子邮件,它可以保证得到验证,因此确实不再存在安全问题。 (2认同)