如何在 Tornado 中使用包含异步回调功能的 Cookie 进行身份验证

64 阅读2分钟

开发人员需要编写一个从远程认证 API 获取异步回调的功能来进行身份验证。简单的登录身份验证工作正常,但是使用 Cookie 密钥进行授权却不能正常使用。授权应该检查 Cookie 中是否存在键“lp_login”。即使功能几乎可用,但仍有两个问题需要解决。

huake_00152_.jpg

  1. 解决方案:
  2. on_response 函数中,开发人员想要为每个页面的授权用户设置安全的 Cookie。但是在代码 self.set_secure_cookie("user", user_id) 中,尽管 user_id 返回了正确的 ID,但它却无法运行。问题可能是因为返回的用户名与 API 不同导致无法识别用户。
  3. 另一个问题是,在异步获取 API URL 期间,用户的页面会在 on_response 设置密钥“user”之前加载,并且该页面的未授权部分将带有登录或注册链接。这可能会让用户感到困惑。为了解决这个问题,开发人员可以阻止尝试加载网站第一页的用户继续加载页面。

为了解决这些问题,可以在 Tornado 中使用以下代码,来实现使用 Cookie 密钥进行身份验证的功能:

import tornado.web
import tornado.http
import tornado.escape
import functools
import logging
import urllib

import Author

def upgrade_lp_login_cookie(method):
    @functools.wraps(method)
    def wrapper(self, *args, **kwargs):
        if not self.current_user and self.get_cookie('lp_login'):
            self.upgrade_lp_login(self.async_callback(method, self, *args, **kwargs))
        else:
            return method(self, *args, **kwargs)
    return wrapper


class BaseHandler(tornado.web.RequestHandler):
    def get_current_user(self):
        user_id = self.get_secure_cookie("user")
        if user_id:
            return Author.objects.get(id=int(user_id))

    def upgrade_lp_login(self, callback):
        lp_login = self.get_cookie("lp_login")
        try:
            username, hashed_password = urllib.unquote(lp_login).rsplit(',',1)
        except ValueError:
            # check against malicious clients
            logging.info('invalid lp_login cookie %s' % lp_login)
            return callback()

        url = "http://%(host)s/api/user/username/%s/%s" % (self.request.host, 
                                                        urllib.quote(username), 
                                                        urllib.quote(hashed_password))
        http = tornado.httpclient.AsyncHTTPClient()
        http.fetch(url, self.async_callback(self.finish_upgrade_lp_login, callback))

    def finish_upgrade_lp_login(self, callback, response):
        answer = tornado.escape.json_decode(response.body)
        # username = answer['username']
        if answer['has_valid_credentials']:
            # set for self.current_user, overriding previous output of self.get_current_user()
            self._current_user = Author.objects.get(email=answer["email"])
            # set the cookie for next request
            self.set_secure_cookie("user", str(self.current_user.id))

        # now chain to the real get/post method
        callback()

    @upgrade_lp_login_cookie
    def get(self):
        self.render('template.tmpl')

通过使用此代码,开发人员可以在 Tornado 中实现使用 Cookie 密钥进行身份验证的功能。