在本节中,我们将建立一个应用,询问访客的名字,然后将其存储在安全cookie中,以便之后取出。后续的请求将认出回客,并展示给她一个定制的页面。你将学到login_url参数和tornado.web.authenticated装饰器的相关知识,这将消除在类似应用中经常会涉及到的一些头疼的问题。
1 示例:欢迎回来¶
在这个例子中,我们将只通过存储在安全cookie里的用户名标识一个人。当某人首次在某个浏览器(或cookie过期后)访问我们的页面时,我们展示一个登录表单页面。表单作为到LoginHandler路由的POST请求被提交。post方法的主体调用set_secure_cookie()来存储username请求参数中提交的值。
代码清单6-2中的Tornado应用展示了我们本节要讨论的验证函数。LoginHandler类渲染登录表单并设置cookie,而LogoutHandler类删除cookie。
import tornado.httpserver import tornado.ioloop import tornado.web import tornado.options import os.path from tornado.options import define, options define("port", default=8000, help="run on the given port", type=int) class BaseHandler(tornado.web.RequestHandler): def get_current_user(self): return self.get_secure_cookie("username") class LoginHandler(BaseHandler): def get(self): self.render('login.html') def post(self): self.set_secure_cookie("username", self.get_argument("username")) self.redirect("/") class WelcomeHandler(BaseHandler): @tornado.web.authenticated def get(self): self.render('index.html', user=self.current_user) class LogoutHandler(BaseHandler): def get(self): if (self.get_argument("logout", None)): self.clear_cookie("username") self.redirect("/") if __name__ == "__main__": tornado.options.parse_command_line() settings = { "template_path": os.path.join(os.path.dirname(__file__), "templates"), "cookie_secret": "bZJc2sWbQLKos6GkHn/VB9oXwQt8S0R0kRvJ5/xJ89E=", "xsrf_cookies": True, "login_url": "/login" } application = tornado.web.Application([ (r'/', WelcomeHandler), (r'/login', LoginHandler), (r'/logout', LogoutHandler) ], **settings) http_server = tornado.httpserver.HTTPServer(application) http_server.listen(options.port) tornado.ioloop.IOLoop.instance().start()
代码清单6-3和6-4是应用templates/目录下的文件。
<html> <head> <title>Please Log In</title> </head> <body> <form action="/login" method="POST"> {% raw xsrf_form_html() %} Username: <input type="text" name="username" /> <input type="submit" value="Log In" /> </form> </body> </html>
<html> <head> <title>Welcome Back!</title> </head> <body> <h1>Welcome back, {{ user }}</h1> </body> </html>
2 authenticated装饰器¶
为了使用Tornado的认证功能,我们需要对登录用户标记具体的处理函数。我们可以使用@tornado.web.authenticated装饰器完成它。当我们使用这个装饰器包裹一个处理方法时,Tornado将确保这个方法的主体只有在合法的用户被发现时才会调用。让我们看看例子中的WelcomeHandler吧,这个类只对已登录用户渲染index.html模板。
class WelcomeHandler(BaseHandler): @tornado.web.authenticated def get(self): self.render('index.html', user=self.current_user)
在get方法被调用之前,authenticated装饰器确保current_usr属性有值。(我们将简短的讨论这个属性。)如果current_user值为假(None、False、0、""),任何GET或HEAD请求都将把访客重定向到应用设置中login_url指定的URL。此外,非法用户的POST请求将返回一个带有403(Forbidden)状态的HTTP响应。
如果发现了一个合法的用户,Tornado将如期调用处理方法。为了实现完整功能,authenticated装饰器依赖于current_user属性和login_url设置,我们将在下面看到具体讲解。
2.1 current_user属性¶
请求处理类有一个current_user属性(同样也在处理程序渲染的任何模板中可用)可以用来存储为当前请求进行用户验证的标识。其默认值为None。为了authenticated装饰器能够成功标识一个已认证用户,你必须覆写请求处理程序中默认的get_current_user()方法来返回当前用户。
实际的实现由你决定,不过在这个例子中,我们只是从安全cookie中取出访客的姓名。很明显,你希望使用一个更加鲁棒的技术,但是出于演示的目的,我们将使用下面的方法:
class BaseHandler(tornado.web.RequestHandler): def get_current_user(self): return self.get_secure_cookie("username")
尽管这里讨论的例子并没有在存储和取出用户密码或其他凭证上有所深入,但本章中讨论的技术可以以最小的额外努力来扩展到查询数据库中的认证。
2.2 login_url设置¶
让我们简单看看应用的构造函数。记住这里我们传递了一个新的设置给应用:login_url是应用登录表单的地址。如果get_current_user方法返回了一个假值,带有authenticated装饰器的处理程序将重定向浏览器的URL以便登录。
settings = { "template_path": os.path.join(os.path.dirname(__file__), "templates"), "cookie_secret": "bZJc2sWbQLKos6GkHn/VB9oXwQt8S0R0kRvJ5/xJ89E=", "xsrf_cookies": True, "login_url": "/login" } application = tornado.web.Application([ (r'/', WelcomeHandler), (r'/login', LoginHandler), (r'/logout', LogoutHandler) ], **settings)
当Tornado构建重定向URL时,它还会给查询字符串添加一个next参数,其中包含了发起重定向到登录页面的URL资源地址。你可以使用像self.redirect(self.get_argument('next', '/'))这样的行来重定向登录后用户回到的页面。