如何在web应用程序中管理多个身份验证服务(Google、Facebook、Twitter)

2024-06-28 19:51:01 发布

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

我正在实现一个googleappengine应用程序,它实现了多重身份验证(google、Facebook和Twitter)。 我在NDB中有一个用户实体,每个身份验证服务有一个ID。 问题是用户可能使用两个不同的服务登录,发布不同的数据(与当前用户具有createdBy关系),然后决定合并两个不同的id。当发生这种情况时,我现在查看用户创建的任何实体,并更改createdBy关系,使其指向合并的用户。 我想知道有没有更聪明、更快速、更标准的方法来处理这种情况。在


Tags: 数据用户实体身份验证id应用程序facebook关系
2条回答

我没有标准的方法知道。两个非常好的库是simpleauth和/或engineauth。《engineauth》(凯尔)的作者也许是一个很好的人来问这个问题。在

它是可行的,但最好不要重新发明一个新的用户模型和使用webapp2_extras良好的用户模型。在

class AuthHandler(BaseRequestHandler, SimpleAuthHandler):
    """Authentication handler for OAuth 2.0, 1.0(a) and OpenID."""

    # Enable optional OAuth 2.0 CSRF guard
    OAUTH2_CSRF_STATE = True

    USER_ATTRS = {
        'facebook': {
            'id': lambda id: ('avatar_url',
                              'http://graph.facebook.com/{0}/picture?type=large'.format(id)),
            'name': 'name',
            'link': 'link'
        },
        'google': {
            'picture': 'avatar_url',
            'name': 'name',
            'profile': 'link'
        },
        'windows_live': {
            'avatar_url': 'avatar_url',
            'name': 'name',
            'link': 'link'
        },
        'twitter': {
            'profile_image_url': 'avatar_url',
            'screen_name': 'name',
            'link': 'link'
        },
        'linkedin': {
            'picture-url': 'avatar_url',
            'first-name': 'name',
            'public-profile-url': 'link'
        },
        'linkedin2': {
            'picture-url': 'avatar_url',
            'first-name': 'name',
            'public-profile-url': 'link'
        },
        'foursquare': {
            'photo': lambda photo: ('avatar_url', photo.get('prefix') + '100x100' + photo.get('suffix')),
            'firstName': 'firstName',
            'lastName': 'lastName',
            'contact': lambda contact: ('email', contact.get('email')),
            'id': lambda id: ('link', 'http://foursquare.com/user/{0}'.format(id))
        },
        'openid': {
            'id': lambda id: ('avatar_url', '/img/missing-avatar.png'),
            'nickname': 'name',
            'email': 'link'
        }
    }

    def _on_signin(self, data, auth_info, provider):
        """Callback whenever a new or existing user is logging in.
         data is a user info dictionary.
         auth_info contains access token or oauth token and secret.
        """
        auth_id = '%s:%s' % (provider, data['id'])
        logging.info('Looking for a user with id %s', auth_id)

        user = self.auth.store.user_model.get_by_auth_id(auth_id)
        _attrs = self._to_user_model_attrs(data, self.USER_ATTRS[provider])

        if user:
            logging.info('Found existing user to log in')
            # Existing users might've changed their profile data so we update our
            # local model anyway. This might result in quite inefficient usage
            # of the Datastore, but we do this anyway for demo purposes.
            #
            # In a real app you could compare _attrs with user's properties fetched
            # from the datastore and update local user in case something's changed.
            user.populate(**_attrs)
            user.put()
            self.auth.set_session(
                self.auth.store.user_to_dict(user))

        else:
            # check whether there's a user currently logged in
            # then, create a new user if nobody's signed in,
            # otherwise add this auth_id to currently logged in user.

            if self.logged_in:
                logging.info('Updating currently logged in user')

                u = self.current_user
                u.populate(**_attrs)
                # The following will also do u.put(). Though, in a real app
                # you might want to check the result, which is
                # (boolean, info) tuple where boolean == True indicates success
                # See webapp2_extras.appengine.auth.models.User for details.
                u.add_auth_id(auth_id)

            else:
                logging.info('2 Creating a brand new user')
                ok, user = self.auth.store.user_model.create_user(auth_id, **_attrs)
                logging.info('2 created a brand new user %s | %s ' % (ok, user))
                if ok:
                    self.auth.set_session(self.auth.store.user_to_dict(user))

        # Remember auth data during redirect, just for this demo. You wouldn't
        # normally do this.
        #self.session.add_flash(data, 'data - from _on_signin(...)')
        #self.session.add_flash(auth_info, 'auth_info - from _on_signin(...)')
        user_dict = self.auth.get_user_by_session()
        logging.info('the user_dict | %s ' % user_dict)
        # Go to the profile page
        self.redirect('/profile')

    def logout(self):
        self.auth.unset_session()
        self.redirect('/')

    def handle_exception(self, exception, debug):
        logging.error(exception)
        self.render('error.html', {'exception': exception})

    def _callback_uri_for(self, provider):
        return self.uri_for('auth_callback', provider=provider, _full=True)

    def _get_consumer_info_for(self, provider):
        """Returns a tuple (key, secret) for auth init requests."""
        return secrets.AUTH_CONFIG[provider]

    def _to_user_model_attrs(self, data, attrs_map):
        """Get the needed information from the provider dataset."""
        user_attrs = {}
        for k, v in attrs_map.iteritems():
            attr = (v, data.get(k)) if isinstance(v, str) else v(data.get(k))
            user_attrs.setdefault(*attr)

        return user_attrs

因此,可以只使用连接创建一个“连接模型”(连接提供程序的列表),与一直在进行此连接的项目(simpleauth和engineauth)进行协商,或者查看上面的代码,然后放入一个可以连接两个帐户的方法。在

这正是Google Identity Toolkit解决的问题。它提供了一种与流行的身份提供者集成的简单方法,并为用户分配一个唯一的ID,无论他/她选择使用哪个登录到您的网站。如果您对Google Identity Toolkit有任何疑问,请将电子邮件发送至discussion group。在

相关问题 更多 >