Django+Google联合登录

2024-05-18 06:11:32 发布

您现在位置:Python中文网/ 问答频道 /正文

我想让我的网站访问者使用他们的谷歌帐户登录,而不是必须注册和创建一个新的。在

几件事:

  • 我没有使用Django身份验证框架,而是自己进行身份验证,并在自己的一组表中保存有关用户的信息
  • 因此,各种django openid库不适用,因为它们都假定使用标准的django auth框架。在

我试图研究python openid库+google联合登录API,但是我迷路了。将会话作为实例来理解,但不理解作为请求关闭的会话。我无法理解看似简单却又如此复杂的事情。在纯python或django中,真的没有一个循序渐进的教程吗?在

我试着看看例子/消费者.py在python openid中,但是它还是有500行代码我不明白。在

我也不明白如何验证用户对谷歌帐户的每一个请求,我的网站。googleapi只解释初始登录步骤。如果我的网站的每个请求都必须通过google服务器进行验证,会发生什么情况?在


Tags: django用户框架auth身份验证api信息标准
2条回答

我认为您的问题源于对OpenID和/或OAuth如何工作的基本误解。在

看起来您只需要身份验证,所以现在让我们继续使用OpenID。查看现有库是正确的。如果只需要openid而不需要OAuth,并且没有使用Django的内置auth框架,那么python openid就是一个可以使用的工具。在

使用OpenID和OAuth进行联合登录的完整文档如下:http://code.google.com/apis/accounts/docs/OpenID.html。特别是,查看“交互序列”下的图表。在

首先,以下是Facebook Tornado web服务器的auth模块的一个非常好的工作示例:

https://github.com/facebook/tornado/blob/master/tornado/auth.py (grep那是“GoogleHandler”的意思。我用得很成功。) 这是独立于Django和Django auth的,应该给您一个很好的例子来说明如何实现您想要的。如果还不够,继续读。。。在

你说django openid无关紧要,但实际上它演示了一个完全符合您需要的实现,但是对于django的auth系统,而不是您的系统。实际上,您应该看看类似的插件Django-SocialAuth,它为一些不同的提供商(Google、Facebook、Twitter等)实现OpenID+OAuth。尤其要注意:

https://github.com/agiliq/Django-Socialauth/blob/master/socialauth/lib/oauthgoogle.pyhttps://github.com/agiliq/Django-Socialauth/tree/master/openid_consumerhttps://github.com/agiliq/Django-Socialauth/tree/master/example_project

…对于一个使用django的auth框架的完整工作示例,可以根据您的自定义auth框架进行调整。在

祝你好运。我鼓励你记录下对你有用的东西,并为像你这样的人建立一个循序渐进的指南。在

我已经设法解决了这个问题,所以这里是解决方案,我希望其他人能从中受益: 1) 谷歌帐户验证不是针对谷歌帐户服务器对你的应用程序的每一个请求。例如: 1.1用户使用gmail帐户登录到你的应用程序 1.2用户还可以导航到gmail.com网站他们检查电子邮件的地方 1.3他们从gmail注销 1.4他们仍然登录到您的应用程序中,并且可以完全使用它 这意味着你必须在你的终端上处理会话到期问题,而Google帐户不负责它。在

2)我使用的Python核心代码如下:

from openid.consumer.consumer import Consumer, \
    SUCCESS, CANCEL, FAILURE, SETUP_NEEDED
from openid.consumer.discover import DiscoveryFailure
from django.utils.encoding import smart_unicode
from myapp.common.util.openid import DjangoOpenIDStore

def google_signin(request):
    """ This is the view where the Google account login icon on your site points to, e.g. http://www.yourdomain.com/google-signin """
    consumer = Consumer(request.session, DjangoOpenIDStore())

    # catch Google Apps domain that is referring, if any 
    _domain = None
    if 'domain' in request.POST:
        _domain = request.POST['domain']
    elif 'domain' in request.GET:
        _domain = request.GET['domain']

    try:
        # two different endpoints depending on whether the using is using Google Account or Google Apps Account
        if _domain:
            auth_request = consumer.begin('https://www.google.com/accounts/o8/site-xrds?hd=%s' % _domain)
        else:
            auth_request = consumer.begin('https://www.google.com/accounts/o8/id')
    except DiscoveryFailure as e:
        return CustomError(request, "Google Accounts Error", "Google's OpenID endpoint is not available.")

    # add requests for additional account information required, in my case: email, first name & last name
    auth_request.addExtensionArg('http://openid.net/srv/ax/1.0', 'mode', 'fetch_request')
    auth_request.addExtensionArg('http://openid.net/srv/ax/1.0', 'required', 'email,firstname,lastname')
    auth_request.addExtensionArg('http://openid.net/srv/ax/1.0', 'type.email', 'http://schema.openid.net/contact/email')
    auth_request.addExtensionArg('http://openid.net/srv/ax/1.0', 'type.firstname', 'http://axschema.org/namePerson/first')
    auth_request.addExtensionArg('http://openid.net/srv/ax/1.0', 'type.lastname', 'http://axschema.org/namePerson/last')

    return redirect(auth_request.redirectURL('http://www.yourdomain.com', 'http://www.yourdomain.com/google-signin-response')))


@transaction.commit_manually 
def google_signin_response(request):
    """ Callback from Google Account service with login the status. Your url could be http://www.yourdomain.com/google-signin-response """
    transaction.rollback() # required due to Django's transaction inconsistency between calls
    oidconsumer = Consumer(request.session, DjangoOpenIDStore())

    # parse GET parameters submit them with the full url to consumer.complete
    _params = dict((k,smart_unicode(v)) for k, v in request.GET.items())
    info = oidconsumer.complete(_params, request.build_absolute_uri().split('?')[0])
    display_identifier = info.getDisplayIdentifier()

    if info.status == FAILURE and display_identifier:
        return CustomError(request, _("Google Login Error"), _("Verification of %(user)s failed: %(error_message)s") % {'user' : display_identifier, 'error_message' : info.message})

    elif info.status == SUCCESS:
        try:
            _email = info.message.args[('http://openid.net/srv/ax/1.0', 'value.email')]
            _first_name = info.message.args[('http://openid.net/srv/ax/1.0', 'value.firstname')]
            _last_name = info.message.args[('http://openid.net/srv/ax/1.0', 'value.lastname')]
            try:
                _user = User.objects.get(email__iexact=_email)
            except ObjectDoesNotExist:
                # create a new account if one does not exist with the authorized email yet and log that user in
                _new_user = _new_account(_email, _first_name + ' ' + _last_name, _first_name, _last_name, p_account_status=1)
                _login(request, _new_user, info.message.args[('http://specs.openid.net/auth/2.0', 'response_nonce')])
                transaction.commit()
                return redirect('home')
            else:
                # login existing user
                _login(request, _user, info.message.args[('http://specs.openid.net/auth/2.0', 'response_nonce')])
                transaction.commit()
                return redirect('home')
        except Exception as e:
            transaction.rollback()
            system_log_entry(e, request=request)
            return CustomError(request, _("Login Unsuccessful"), "%s" % e)

    elif info.status == CANCEL:
        return CustomError(request, _("Google Login Error"), _('Google account verification cancelled.'))

    elif info.status == SETUP_NEEDED:
        if info.setup_url:
            return CustomError(request, _("Google Login Setup Needed"), _('<a href="%(url)s">Setup needed</a>') % { 'url' : info.setup_url })
        else:
            # This means auth didn't succeed, but you're welcome to try
            # non-immediate mode.
            return CustomError(request, _("Google Login Setup Needed"), _('Setup needed'))
    else:
        # Either we don't understand the code or there is no
        # openid_url included with the error. Give a generic
        # failure message. The library should supply debug
        # information in a log.
        return CustomError(request, _("Google Login Error"), _('Google account verification failed for an unknown reason. Please try to create a manual account on Acquee.'))


def get_url_host(request):
    if request.is_secure():
        protocol = 'https'
    else:
        protocol = 'http'
    host = escape(get_host(request))
    return '%s://%s' % (protocol, host)

3)我在上面创建并导入了一个附加的lib(myapp.common.util.openid)是一些现有的Django openid库的合并,因此向这些人致敬:

^{pr2}$

4)以及保存google帐户会话标识符和已验证端点所需的模型:

class Nonce(models.Model):
    """ Required for OpenID functionality """
    server_url = models.CharField(max_length=255)
    timestamp = models.IntegerField()
    salt = models.CharField(max_length=40)

    def __unicode__(self):
        return u"Nonce: %s for %s" % (self.salt, self.server_url)


class Association(models.Model):
    """ Required for OpenID functionality """
    server_url = models.TextField(max_length=2047)
    handle = models.CharField(max_length=255)
    secret = models.TextField(max_length=255) # Stored base64 encoded
    issued = models.IntegerField()
    lifetime = models.IntegerField()
    assoc_type = models.TextField(max_length=64)

    def __unicode__(self):
        return u"Association: %s, %s" % (self.server_url, self.handle)

祝你好运! 韩国

相关问题 更多 >