Tornado入门(七)【认证和安全】

Cookies和安全Cookies

通过set_cookie来设置浏览器中的cookies信息。

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!")

Cookies信息通常不安全,很容易被篡改。如果需要通过cookies来区分不同的登录用户,则需要对cookies进行签名,以防伪造。Tornado通过set_secure_cookieget_secure_cookie方法支持签名Cookies。为了使用这两个方法,需要在应用中配置cookie_secret

application = tornado.web.Application([
    (r"/", MainHandler),
], cookie_secret="__TODO:_GENERATE_YOUR_OWN_RANDOM_VALUE_HERE__")

签过名的cookies包含了编码之后的cookie值,时间戳,和一个HMAC签名。如果cookie过期或者签名不匹配,则get_secure_cookie将返回None

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是一个对称密钥,所以必须确保它的安全。

默认情况下,Tornado cookies的生存期为30天,如果需要修改,可以将参数expire_days传递给set_secure_cookiemax_age_days参数传递给get_secure_cookie。这个两个参数是分别传递的。

用户认证

认证过的用户可以通过self.current_user访问,在模板中可以通过current_user访问,默认情况下current_userNone

为了实现用户认证,需要重写处理器中的get_current_user方法,以确定当前的用户。下面是一个简单的示例。

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('<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.redirect("/")

application = tornado.web.Application([
    (r"/", MainHandler),
    (r"/login", LoginHandler),
], cookie_secret="__TODO:_GENERATE_YOUR_OWN_RANDOM_VALUE_HERE__")

通过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)

第三方认证

tornado.auth模块实现了第三方的认证协议,包括Google和Facebook等。

下面是使用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'})

跨站请求伪造

跨站请求伪造是很多个人网站都会遇到的安全问题。

通常防止XSRF的方法是传递一个不可预测的cookie值给用户,然后用户提交表单时携带这个值。如果cookie信息-和表中的值不匹配,则说明请求时可能是伪造的。

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应用会为所有的用户设置_xsrfcookie,并拒绝所有没有携带正确的_xsrf字段的POSTPUT或者DELETE请求。注意需要确保所有提交的表单都包含了这个字段。可以使用UIModule xsrf_form_html()

<form action="/new_message" method="post">
  {% module xsrf_form_html() %}
  <input type="text" name="message"/>
  <input type="submit" value="Post"/>
</form>

如果是通过Ajax提交表单,同样需要确保提供了_xsrf字段 :

function getCookie(name) {
    var r = document.cookie.match("\\b" + name + "=([^;]*)\\b");
    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 + ")"));
    }});
};

对于PUTDELETExsrf_form_html可以通过头部字段X-XSRFToken设置。

如果需要自定义XSRF的行为,可以重写RequestHandler.check_xsrf_cookie方法。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏大内老A

Windows安全认证是如何进行的?[Kerberos篇]

最近一段时间都在折腾安全(Security)方面的东西,比如Windows认证、非对称加密、数字证书、数字签名、TLS/SSL、WS-Security等。如果时...

24770
来自专栏积累沉淀

CXF 框架拦截器

CXF的拦截器 •为什么设计拦截器? 1.为了在webservice请求过程中,能动态操作请求和响应数据, CXF设计了拦截器. •拦截器分类: 1...

20660
来自专栏坚毅的PHP

tornado cherrypy bottle性能测试

2011-11-25 今天做了个tornado cherrypy 和 bottle的对比 ab -n 10000 -c 1000 http://192.1...

40360
来自专栏比原链

Derek解读Bytom源码-P2P网络 地址簿

Gitee地址:https://gitee.com/BytomBlockchain/bytom

9410
来自专栏FreeBuf

Windows DNS API RCE漏洞分析及PoC构造

根据 Microsoft 2017 年 10 月安全通告,多个版本 Windows 中的 dnsapi.dll 在处理 DNS response 时可导致 SY...

389100
来自专栏Kubernetes

原 深入分析Kubernetes Sche

22040
来自专栏landv

杨格门锁 YGSLockSDK V3.3酒管软件接口_99v

27390
来自专栏张善友的专栏

HTTP Basic Authentication验证WCF Data Service

WCF Data Service是OData协议,也是RESTFul Service的一种,上篇文章已经介绍了HTTP Basic Authentication...

21780
来自专栏Java帮帮-微信公众号-技术文章全总结

Web-第二十三天 Web商城实战三【悟空教程】

<a href="${pageContext.request.contextPath}/OrderServlet?method=findByUid">我的订单<...

20510
来自专栏Java帮帮-微信公众号-技术文章全总结

Web-第二十一天 Web商城实战一【悟空教程】

public class BaseServlet extends HttpServlet {

22840

扫码关注云+社区

领取腾讯云代金券