在这个例子中,我们将只通过存储在安全cookie里的用户名标识一个人。当某人首次在某个浏览器(或cookie过期后)访问我们的页面时,我们展示一个登录表单页面。表单作为到LoginHandler路由的POST请求被提交。post方法的主体调用set_secure_cookie()来存储username请求参数中提交的值。
代码如下:
class BaseHandler(tornado.web.RequestHandler):
def get_current_user(self):
return self.get_secure_cookie("username")
class LoginHandler(BaseHandler):
def get(self, *args, **kwargs):
self.render('login.html')
def post(self, *args, **kwargs):
self.set_secure_cookie("username",self.get_argument("username"))
class WelcomeHandler(BaseHandler):
@tornado.web.authenticated
def get(self, *args, **kwargs):
self.render('index.html',user=self.current_user)
class LogoutHandler(BaseHandler):
def get(self, *args, **kwargs):
if (self.get_argument("logout",None)):
self.clear_cookie("username")
self.redirect('/')
def server_function():
cookie='bZJc2sWbQLKos6GkHn/VB9oXwQt8S0R0kRvJ5/xJ89E='
tornado.options.parse_command_line()
app = tornado.web.Application(handlers=[(r"/", WelcomeHandler),(r"/login",LoginHandler),(r"/logout",LogoutHandler)],template_path=os.path.join(os.path.dirname(__file__),"template"),
static_path=os.path.join(os.path.dirname(__file__),"static"),ui_modules={'Book':HelloModule},cookie_secret=cookie,xsrf_cookies=True,login_url="/login")
http_server = tornado.httpserver.HTTPServer(app)
http_server.listen(options.port,address='127.0.0.1')
tornado.ioloop.IOLoop.instance().start()
首先来看下代码的运行原理:
第一步:首先来看WelcomeHandler。这个类的get函数被@tornado.web.authenticated装饰器给装饰,我们进入这个装饰器的代码看下具体的实现:
def authenticated(method):
@functools.wraps(method)
def wrapper(self, *args, **kwargs):
if not self.current_user:
if self.request.method in ("GET", "HEAD"):
url = self.get_login_url()
if "?" not in url:
if urlparse.urlsplit(url).scheme:
# if login url is absolute, make next absolute too
next_url = self.request.full_url()
else:
next_url = self.request.uri
url += "?" + urlencode(dict(next=next_url))
self.redirect(url)
return
raise HTTPError(403)
return method(self, *args, **kwargs)
return wrapper
第二步:authenticated首先判断self.current_user是否存在,self.crrent_user的值在tornado.web.RequestHandler中获得。这是一个RequestHandler的类属性。返回的是_current_user,而且_current_user 是通过self.get_current_user()获取的
@property
def current_user(self):
if not hasattr(self, "_current_user"):
self._current_user = self.get_current_user()
return self._current_user
而我们在BaseHandler中重写了get_current_user,从客户端发送的cookie中获取username的值。那么如果是第一次访问或者是cookie值失效后,得到的current_user将会是空
def get_current_user(self):
return self.get_secure_cookie("username")
第三步:如果current_user为空,网页将重定向到login_url设置的页面连接。url = self.get_login_url()。在这个函数中通过return self.application.settings["login_url"]
返回重定向的url
def get_login_url(self):
self.require_setting("login_url", "@tornado.web.authenticated")
return self.application.settings["login_url"]
第四步:进入登录界面的处理函数LoginHandler。在登录界面中将进行username的cookie值设置
class LoginHandler(BaseHandler):
def get(self, *args, **kwargs):
self.render('login.html')
def post(self, *args, **kwargs):
self.set_secure_cookie("username",self.get_argument("username"))
第五步:再次登录,由于current_user已经存在,因此跳转到index.html网页,并显示登录的用户名
class WelcomeHandler(BaseHandler):
@tornado.web.authenticated
def get(self, *args, **kwargs):
self.render('index.html',user=self.current_user
代码已经解释完了,我们来看下具体的网页访问例子:
第一步:浏览器中输入http://127.0.0.1:8003/,将会自动跳转到http://127.0.0.1:8003/login?next=%2F
后台打印:
[I 180105 10:11:05 web:2063] 302 GET / (127.0.0.1) 0.33ms
[I 180105 10:11:05 web:2063] 200 GET /login?next=%2F (127.0.0.1) 0.68ms
浏览器中查看cookie值,此时只有设置的_xsrf值
第二步:此时在网页中输入用户名并点击login。再次查看cookie值,已经存在了username的cookie值
第三步 再次登录http://127.0.0.1:8003/。可以看到没有跳转到登录界面,而是直接显示了登录用户。
整个后台的打印:
[I 180105 10:11:05 web:2063] 302 GET / (127.0.0.1) 0.33ms
[I 180105 10:11:05 web:2063] 200 GET /login?next=%2F (127.0.0.1) 0.68ms
[I 180105 10:14:50 web:2063] 200 POST /login (127.0.0.1) 1.46ms
[I 180105 10:16:17 web:2063] 200 GET / (127.0.0.1) 0.64ms