7.3 示例:Facebook 认证和 Graph API
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 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论