返回介绍

7.3 示例:Facebook 认证和 Graph API

发布于 2025-03-08 19:24:52 字数 6738 浏览 0 评论 0 收藏 0

Facebook 的这个例子在结构上和刚才看到的 Twitter 的例子非常相似。Facebook 有两种不同的 API 标准,原始的 REST API 和 Facebook Graph API。目前两种 API 都被支持,但 Graph API 被推荐作为开发新 Facebook 应用的方式。Tornado 在 auth 模块中支持这两种 API,但在这个例子中我们将关注 Graph API。

为了开始这个例子,你需要登录到 Facebook 的 开发者网站 ,并创建一个新的应用。你将需要填写应用的名称,并证明你不是一个机器人。为了从你自己的域名中验证用户,你还需要指定你应用的域名。然后点击"Select how your app integrates with Facbook"下的"Website"。同时你需要输入你网站的 URL。要获得完整的创建 Facebook 应用的手册,可以从 https://developers.facebook.com/docs/guides/web/ 开始。

你的应用建立好之后,你将使用基本设置页面的应用 ID 和 secret 来连接 Facebook Graph API。

回想一下上一节的提到的单一登录工作流程,它将引导用户到 Facebook 平台验证应用,Facebook 将使用一个 HTTP 重定向将一个带有验证码的用户返回给你的服务器。一旦你接收到含有这个认证码的请求,你必须请求用于标识 API 请求用户身份的验证令牌。

这个例子将渲染用户的时间轴,并允许用户通过我们的接口更新她的 Facebook 状态。让我们看下代码清单 7-4。

代码清单 7-4 Facebook 验证:facebook.py

import tornado.web
import tornado.httpserver
import tornado.auth
import tornado.ioloop
import tornado.options
from datetime import datetime

class FeedHandler(tornado.web.RequestHandler, tornado.auth.FacebookGraphMixin):
    @tornado.web.asynchronous
    def get(self):
        accessToken = self.get_secure_cookie('access_token')
        if not accessToken:
            self.redirect('/auth/login')
            return

        self.facebook_request(
            "/me/feed",
            access_token=accessToken,
            callback=self.async_callback(self._on_facebook_user_feed))

    def _on_facebook_user_feed(self, response):
        name = self.get_secure_cookie('user_name')
        self.render('home.html', feed=response['data'] if response else [], name=name)

    @tornado.web.asynchronous
    def post(self):
        accessToken = self.get_secure_cookie('access_token')
        if not accessToken:
            self.redirect('/auth/login')

        userInput = self.get_argument('message')

        self.facebook_request(
            "/me/feed",
            post_args={'message': userInput},
            access_token=accessToken,
            callback=self.async_callback(self._on_facebook_post_status))

    def _on_facebook_post_status(self, response):
        self.redirect('/')

class LoginHandler(tornado.web.RequestHandler, tornado.auth.FacebookGraphMixin):
    @tornado.web.asynchronous
    def get(self):
        userID = self.get_secure_cookie('user_id')

        if self.get_argument('code', None):
            self.get_authenticated_user(
                redirect_uri='http://example.com/auth/login',
                client_id=self.settings['facebook_api_key'],
                client_secret=self.settings['facebook_secret'],
                code=self.get_argument('code'),
                callback=self.async_callback(self._on_facebook_login))
            return
        elif self.get_secure_cookie('access_token'):
            self.redirect('/')
            return

        self.authorize_redirect(
            redirect_uri='http://example.com/auth/login',
            client_id=self.settings['facebook_api_key'],
            extra_params={'scope': 'read_stream,publish_stream'}
        )

    def _on_facebook_login(self, user):
        if not user:
            self.clear_all_cookies()
            raise tornado.web.HTTPError(500, 'Facebook authentication failed')

        self.set_secure_cookie('user_id', str(user['id']))
        self.set_secure_cookie('user_name', str(user['name']))
        self.set_secure_cookie('access_token', str(user['access_token']))
        self.redirect('/')

class LogoutHandler(tornado.web.RequestHandler):
    def get(self):
        self.clear_all_cookies()
        self.render('logout.html')

class FeedListItem(tornado.web.UIModule):
    def render(self, statusItem):
        dateFormatter = lambda x: datetime.
strptime(x,'%Y-%m-%dT%H:%M:%S+0000').strftime('%c')
        return self.render_string('entry.html', item=statusItem, format=dateFormatter)

class Application(tornado.web.Application):
    def __init__(self):
        handlers = [
            (r'/', FeedHandler),
            (r'/auth/login', LoginHandler),
                (r'/auth/logout', LogoutHandler)
            ]

            settings = {
                'facebook_api_key': '2040 ... 8759',
                'facebook_secret': 'eae0 ... 2f08',
                'cookie_secret': 'NTliOTY5NzJkYTVlMTU0OTAwMTdlNjgzMTA5M2U3OGQ5NDIxZmU3Mg==',
                'template_path': 'templates',
                'ui_modules': {'FeedListItem': FeedListItem}
            }

            tornado.web.Application.__init__(self, handlers, **settings)

    if __name__ == '__main__':
        tornado.options.parse_command_line()

        app = Application()
        server = tornado.httpserver.HTTPServer(app)
        server.listen(8000)
        tornado.ioloop.IOLoop.instance().start()

我们将按照访客与应用交互的顺序来讲解这些处理。当请求根 URL 时,FeedHandler 将寻找 access_token cookie。如果这个 cookie 不存在,用户会被重定向到/auth/login URL。

登录页面使用了 authorize_redirect 方法来讲用户重定向到 Facebook 的验证对话框,如果需要的话,用户在这里登录 Facebook,审查应用程序请求的权限,并批准应用。在点击"Approve"之后,她将被跳转回应用在 authorize_redirect 调用中 redirect_uri 指定的 URL。

当从 Facebook 验证页面返回后,到/auth/login 的请求将包括一个 code 参数作为查询字符串参数。这个码是一个用于换取永久凭证的临时令牌。如果发现了 code 参数,应用将发出一个 Facebook Graph API 请求来取得认证的用户,并存储她的用户 ID、全名和访问令牌,以便在应用发起 Graph API 调用时标识该用户。

存储了这些值之后,用户被重定向到根 URL。用户这次回到根页面时,将取得最新 Facebook 消息列表。应用查看 access_cookie 是否被设置,并使用 facebook_request 方法向 Graph API 请求用户订阅。我们把 OAuth 令牌传递给 facebook_request 方法,此外,这个方法还需要一个回调函数参数--在代码清单 7-4 中,它是_on_facebook_user_feed 方法。

代码清单 7-5 Facebook 验证:home.html

<html>
    <head>
        <title>{{ name }} on Facebook</title>
    </head>

    <body>
        <div>
            <a href="/auth/logout">Sign out</a>
            <h1>{{ name }}</h1>
        </div>
        <div>
            <form action="/facebook/" method="POST">
                <textarea rows="3" cols="50" name="message"></textarea>
                <input type="submit" value="Update Status" />
            </form>
        </div>
        <hr />
        {% for item in feed %}
            {% module FeedListItem(item) %}
        {% end %}
    </body>
</html>

当包含来自 Facebook 的用户订阅消息的响应的回调函数被调用时,应用渲染 home.html 模板,其中使用了 FeedListItem 这个 UI 模块来渲染列表中的每个条目。在模板开始处,我们渲染了一个表单,可以用 message 参数 post 到我们服务器的/resource。应用发送这个调用给 Graph API 来发表一个更新。

为了发表更新,我们再次使用了 facebook_request 方法。这次,除了 access_token 参数之外,我们还包括了一个 post_args 参数,这个参数是一个成为 Graph 请求 post 主体的参数字典。当调用成功时,我们将用户重定向回首页,并请求更新后的时间轴。

正如你所看到的,Tornado 的 auth 模块提供的 Facebook 验证类包括很多构建 Facebook 应用时非常有用的功能。这不仅在原型设计中是一笔巨大的财富,同时也非常适合是生产中的应用。

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文