摘要:译者说于年月日发布,该版本正式支持的关键字,并且用旧版本编译同样可以使用这两个关键字,这无疑是一种进步。其次,这是最后一个支持和的版本了,在后续的版本了会移除对它们的兼容。
译者说
Tornado 4.3于2015年11月6日发布,该版本正式支持Python3.5的async/await关键字,并且用旧版本CPython编译Tornado同样可以使用这两个关键字,这无疑是一种进步。其次,这是最后一个支持Python2.6和Python3.2的版本了,在后续的版本了会移除对它们的兼容。现在网络上还没有Tornado4.3的中文文档,所以为了让更多的朋友能接触并学习到它,我开始了这个翻译项目,希望感兴趣的小伙伴可以一起参与翻译,项目地址是tornado-zh on Github,翻译好的文档在Read the Docs上直接可以看到。欢迎Issues or PR。
认证和安全 Cookies 和 secure cookies你可以在用户浏览器中通过set_cookie方法设置 cookie:
class MainHandler(tornado.web.RequestHandler): def get(self): if not self.get_cookie("mycookie"): self.set_cookie("mycookie", "myvalue") self.write("Your cookie was not set yet!") else: self.write("Your cookie was set!")
普通的cookie并不安全, 可以通过客户端修改. 如果你需要通过设置cookie,例如来识别当前登录的用户, 就需要给你的cookie签名防止伪造. Tornado支持通过 RequestHandler.set_secure_cookie 和RequestHandler.get_secure_cookie 方法对cookie签名. 想要使用这些方法, 你需要在你创建应用的时候, 指定一个名为cookie_secret的密钥. 你可以在应用的设置中以关键字参数的形式传递给应用程序:
application = tornado.web.Application([ (r"/", MainHandler), ], cookie_secret="__TODO:_GENERATE_YOUR_OWN_RANDOM_VALUE_HERE__")
签名后的cookie除了时间戳和一个HMAC 签名还包含编码后的cookie值. 如果cookie过期或者签名不匹配,get_secure_cookie将返回None就像没有设置cookie一样. 上面例子的安全版本:
class MainHandler(tornado.web.RequestHandler): def get(self): if not self.get_secure_cookie("mycookie"): self.set_secure_cookie("mycookie", "myvalue") self.write("Your cookie was not set yet!") else: self.write("Your cookie was set!")
Tornado的安全cookie保证完整性但是不保证机密性. 也就是说, cookie不能被修改但是它的内容对用户是可见的. 密钥cookie_secret是一个对称的key, 而且必须保密--任何获得这个key的人都可以伪造出自己签名的cookie.
默认情况下, Tornado的安全cookie过期时间是30天. 可以给set_secure_cookie使用expires_days关键字参数 同时get_secure_cookie设置max_age_days参数也可以达到效果. 这两个值分别通过这样(设置)你就可以达到如下的效果, 例如大多数情况下有30天有效期的cookie, 但是对某些敏感操作(例如修改账单信息)你可以使用一个较小的max_age_days.
Tornado也支持多签名密钥, 使签名密钥轮换.cookie_secret然后必须是一个以整数key版本作为key, 以相对应的密钥作为值的字典. 当前使用的签名键必须是 应用设置中key_version的集合. 不过字典中的其他key都允许做cookie签名验证, 如果当前key版本在cookie集合中.为了实现cookie更新, 可以通过RequestHandler.get_secure_cookie_key_version 查询当前key版本.
用户认证当前已经通过认证的用户在每个请求处理函数中都可以通过self.current_user 得到, 在每个模板中可以使用current_user获得. 默认情况下,current_user是None.
为了在你的应用程序中实现用户认证, 你需要在你的请求处理函数中复写get_current_user()方法来判断当前用户, 比如可以基于cookie的值.这里有一个例子, 这个例子允许用户简单的通过一个保存在cookie中的特殊昵称登录到应用程序中:
class BaseHandler(tornado.web.RequestHandler): def get_current_user(self): return self.get_secure_cookie("user") class MainHandler(BaseHandler): def get(self): if not self.current_user: self.redirect("/login") return name = tornado.escape.xhtml_escape(self.current_user) self.write("Hello, " + name) class LoginHandler(BaseHandler): def get(self): self.write("") def post(self): self.set_secure_cookie("user", self.get_argument("name")) self.redirect("/") application = tornado.web.Application([ (r"/", MainHandler), (r"/login", LoginHandler), ], cookie_secret="__TODO:_GENERATE_YOUR_OWN_RANDOM_VALUE_HERE__")
你可以使用 Python装饰器(decorator)tornado.web.authenticated 要求用户登录. 如果请求方法带有这个装饰器并且用户没有登录, 用户将会被重定向到login_url(另一个应用设置).上面的例子可以被重写:
class MainHandler(BaseHandler): @tornado.web.authenticated def get(self): name = tornado.escape.xhtml_escape(self.current_user) self.write("Hello, " + name) settings = { "cookie_secret": "__TODO:_GENERATE_YOUR_OWN_RANDOM_VALUE_HERE__", "login_url": "/login", } application = tornado.web.Application([ (r"/", MainHandler), (r"/login", LoginHandler), ], **settings)
如果你使用authenticated装饰post()方法并且用户没有登录,服务将返回一个403响应.@authenticated装饰器是if not self.current_user: self.redirect()的简写. 可能不适合非基于浏览器的登录方案.
通过 Tornado Blog example application可以看到一个使用用户验证(并且在MySQL数据库中存储用户数据)的完整例子.
第三方用户验证tornado.auth 模块实现了对一些网络上最流行的网站的身份认证和授权协议,包括Google/Gmail, Facebook, Twitter,和FriendFeed. 该模块包括通过这些网站登录用户的方法, 并在适用情况下允许访问该网站服务的方法, 例如, 下载一个用户的地址簿或者在他们支持下发布一条Twitter信息.
这是个使用Google身份认证, 在cookie中保存Google的认证信息以供之后访问的示例处理程序:
class GoogleOAuth2LoginHandler(tornado.web.RequestHandler, tornado.auth.GoogleOAuth2Mixin): @tornado.gen.coroutine def get(self): if self.get_argument("code", False): user = yield self.get_authenticated_user( redirect_uri="http://your.site.com/auth/google", code=self.get_argument("code")) # Save the user with e.g. set_secure_cookie else: yield self.authorize_redirect( redirect_uri="http://your.site.com/auth/google", client_id=self.settings["google_oauth"]["key"], scope=["profile", "email"], response_type="code", extra_params={"approval_prompt": "auto"})
查看 tornado.auth 模块的文档以了解更多细节.
跨站请求伪造(防护)跨站请求伪造(Cross-site request forgery),或XSRF, 是所有web应用程序面临的一个主要问题. 可以通过Wikipedia 文章来了解更多关于XSRF的细节.
普遍接受的预防XSRF攻击的方案是让每个用户的cookie都是不确定的值, 并且把那个cookie值在你站点的每个form提交中作为额外的参数包含进来. 如果cookie和form提交中的值不匹配, 则请求可能是伪造的.
Tornado内置XSRF保护. 你需要在你的应用设置中使用xsrf_cookies便可以在你的网站上使用:
settings = { "cookie_secret": "__TODO:_GENERATE_YOUR_OWN_RANDOM_VALUE_HERE__", "login_url": "/login", "xsrf_cookies": True, } application = tornado.web.Application([ (r"/", MainHandler), (r"/login", LoginHandler), ], **settings)
如果设置了xsrf_cookies, Tornado web应用程序将会给所有用户设置_xsrfcookie并且拒绝所有不包含一个正确的_xsrf值的POST,PUT, 或DELETE请求. 如果你打开这个设置, 你必须给所有通过POST请求的form提交添加这个字段. 你可以使用一个特性的UIModule`xsrf_form_html()`来做这件事情, 这个方法在所有模板中都是可用的:
如果你提交一个AJAX的POST请求, 你也需要在每个请求中给你的JavaScript添加_xsrf值. 这是我们在FriendFeed为了AJAX的POST请求使用的一个 jQuery函数,可以自动的给所有请求添加_xsrf值:
function getCookie(name) { var r = document.cookie.match("" + name + "=([^;]*)"); return r ? r[1] : undefined; } jQuery.postJSON = function(url, args, callback) { args._xsrf = getCookie("_xsrf"); $.ajax({url: url, data: $.param(args), dataType: "text", type: "POST", success: function(response) { callback(eval("(" + response + ")")); }}); };
对于PUT和DELETE请求(除了不使用form编码(form-encoded) 参数的POST请求, XSRF token也会通过一个X-XSRFToken的HTTP头传递.XSRF cookie 通常在使用xsrf_form_html会设置, 但是在不使用正规form的纯Javascript应用中, 你可能需要访问self.xsrf_token手动设置(只读这个属性足够设置cookie了).
如果你需要自定义每一个处理程序基础的XSRF行为, 你可以复写RequestHandler.check_xsrf_cookie(). 例如, 如果你有一个没有使用cookie验证的API, 你可能想禁用XSRF保护, 可以通过使check_xsrf_cookie()不做任何处理. 然而, 如果你支持基于cookie和非基于cookie的认证, 重要的是,当前带有cookie认证的请求究竟什么时候使用XSRF保护.
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/37721.html
摘要:译者说于年月日发布,该版本正式支持的关键字,并且用旧版本编译同样可以使用这两个关键字,这无疑是一种进步。其次,这是最后一个支持和的版本了,在后续的版本了会移除对它们的兼容。本节感谢翻译本节最好直接在或者阅读,以获得更好的阅读体验格式支持。 译者说 Tornado 4.3于2015年11月6日发布,该版本正式支持Python3.5的async/await关键字,并且用旧版本CPython...
阅读 3020·2021-11-12 10:36
阅读 4726·2021-09-22 10:57
阅读 1558·2021-09-22 10:53
阅读 2636·2019-08-30 15:55
阅读 3492·2019-08-29 17:00
阅读 3351·2019-08-29 16:36
阅读 2462·2019-08-29 13:46
阅读 1348·2019-08-26 11:45