python进阶tornado

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

2017-09-30  本文已影响58人  nummycode

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方法。

上一篇下一篇

猜你喜欢

热点阅读