美文网首页python进阶tornado
Tornado入门(七)【认证和安全】

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

作者: nummycode | 来源:发表于2017-09-30 15:50 被阅读58次

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

    相关文章

      网友评论

        本文标题:Tornado入门(七)【认证和安全】

        本文链接:https://www.haomeiwen.com/subject/bxxnextx.html