身份验证和安全 Cookie 和安全 Cookie 您可以使用 set_cookie 方法在用户浏览器中设置 cookie。
1 2 3 4 5 6 7 class MainHandler (tornado.web.RequestHandler): def get (self ): if not self .get_cookie("mycookie" ): self .set_cookie("mycookie" , "myvalue" ) self .write("Set your cookie success." ) else : self .write("Your cookie is set." )
Cookie 并不安全,并且很容易被客户修改。如果您需要设置 cookie 来识别当前登录的用户,则需要对 cookie 进行签名以防止伪造。 Tornado 支持使用 set_secure_cookie 和 get_secure_cookie 方法签名 cookie。要使用这些方法,您需要在创建应用程序时指定名为 cookie_secure 的安全密钥。您可以将应用程序设置作为关键字参数传递给您的应用程序:
1 2 3 4 application = tornado.web.Application([ (r"/" , MainHandler), ], cookie_secure="ASDASDASDASDDSDADA" )
签名 cookie 除了时间戳和 HMAC 签名之外还包含 cookie 的编码值。如果 cookie 是旧的或者签名不匹配,get_secure_cookie 将返回 None,就像未设置 cookie 一样。上例的安全版本:
1 2 3 4 5 6 7 class MainHandler (tornado.web.RequestHandler): def get (self ): if not self .get_secure_cookie("mycookie" ): self .set_secure_cookie("mycookie" , "myvalue" ) self .wirte("Your cookie was not set yet." ) else : self .write("Your cookie was set." )
Tornado 的安全 cookie 保证完整性,但不保证机密性。也就是说,cookie 不能被修改,但其内容可以被用户看到。 cookie_secret 是对称密钥,必须保证安全。
默认情况下,Tornado 的安全 cookie 会在 30 天后过期。要更改此设置,请使用 set_secure_cookie 的 expires_days 关键字参数和 get_secure_cookie 的 max_age_days 参数。对于某些敏感操作(例如更改账单信息),您在读取 cookie 时使用较小的 max_age_days 。
用户认证 当前经过身份验证的用户在每个请求处理程序中都可以使用 self.current_user 形式,在每个模板中可以使用 current_user 形式。默认情况下,该值为“无”。
要在应用程序中实现用户身份验证,您需要重写请求处理程序中的 get_current_user() 方法,以根据 cookie 的值等确定当前用户。下面是一个示例,用户只需指定昵称即可登录应用程序,然后将昵称保存在 cookie 中:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 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, %s" % name) class LoginHandler (BaseHandler ): def get (self ): self .write('<html><body><form action="/login" method="post">' 'Name: <input type="text" name="name">' '<input type="submit" value="Sign in">' '</form></body></html>' ) def post (self ): self .set_secure_cookie("user" , self .get_argument("name" )) self .redorect("/" ) application = tornado.web.Application([ (r'/' , MainHandler), (r'/login' , LoginHandler), ], cookie_secret="ASDASDAdasdasadawd" )
您可以要求用户使用 Python 装饰器 tornado.web.authenticated** 登录。如果结果转到带有此装饰器的方法,并且用户未登录,他们将被重定向到 login_url。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 class MainHandler (BaseHandler ): @tornado.web.authenticated def get (self ): name = tornado.escape.xhtml_escape(self .current_user) self .write("Hello %s" % name) settings = { "cookie_secret" :"ASDASDASDASDASDAS" , "login_url" :"/login" } application = tornado.web.Application([ (r'/' , MainHandler), (r'/login' , LoginHandler), ], **settings)
如果您使用经过身份验证的装饰器装饰 post() 方法并且用户未登录,则服务器将发送 403 响应。
第三方认证 ornado.auth 模块为网络上许多最受欢迎的站点实现身份验证和授权协议,该协议名为 OAuth2 Login。这是示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 class GoogleOAuth2LoginHandler (tornado.web.RequestHandler, tornado.auth.GoogleOAuth2Mixin): async def get (self ): if self .get_argument("code" , False ): user = await self .get_authenticated_user( redirect_url = "http://yourweb.com" , code = self .get_argument("code" ) ) else : await self .authorize_redirect( redirect_url = "http://youweb.com" , client_id = self .settings["google_oauth" ]["key" ], scope = ["profile" , "email" ], response_type = 'code' , extra_params = {"k" :"v" } )
跨站请求伪造(CSRF/XSRF)防护 CSRF(或 XSRF)是个性化 Web 应用程序的常见问题。普遍接受的防止 CSRF 的解决方案是使用不可预测的值对每个用户进行 cookie 并在表单提交中包含该值,如果不匹配,则该请求很可能是伪造的。
Tornado 具有内置 XSRF 保护。要将其包含在您的网站中,请包含应用程序设置 xsrf_cookies。
1 2 3 4 5 6 7 8 9 10 settings = { "cookie_secret" :"ADADADASDASDASDA" , "login_url" :"./login" , "xsrf_cookie" :True } application = tornado.web.Application([ (r'/' , MainHandler), (r'/login' , LoginHandler) ], **settings)
如果设置了 xsrf_cookie,Tornado Web 应用程序将为所有用户设置 _xsrf cookie,并拒绝所有不包含正确 _xsrf 值的 POST、PUT 和 DELETE 请求。如果打开此设置,则需要检测通过 POST 提交的所有表单以包含此字段。您可以使用所有模板中可用的特殊 UIModule xsrf_form_html() 来执行此操作:
1 2 3 4 5 <form action ="/new_message" method ="post" > {% module sxrf_form_html() %} <input type ="text" name ="message" /> <input type ="submit" value ="post" /> </form >
如果您使用 AJAX 提交,您还需要检测 Javascript,以便在每个请求中包含 _xsrf 值。这是我们在 FriendFeed 中用于 AJAX POST 请求的 jQuery 函数,它会自动将 _xsrf 值添加到所有请求:
1 2 3 4 5 6 7 8 9 10 11 12 function getCookie (name ) { var r = document .cookie .match ("\\b" + name + "=([^;]*)\\b" ); return r ? r[1 ] : undefined ; } jQuery.postJSON = function (url, args, callback ) { args._xrsf = getCookie ("_xsrf" ); $.ajax ({url :url, data : $.params (args), dataType :"text" , type :"POST" , success : function (response ){ callback (eval ("(" + response + ")" )); }}); };
对于 PUT 和 DELETE 请求(以及不使用表单编码参数的 POST 请求),XSRF 令牌也可以通过名为 X-XSRFToken 的 HTTP 标头传递。 XSRF cookie 通常在使用 xsrf_form_html 时设置,但在不使用任何常规形式的纯 Javascript 应用程序中,您可能需要手动访问 self.xsrf_token (仅读取该属性就足以将 cookie 设置为副作用)。
DNS 重新绑定 通过