tornado学习五二:session

页面的Session检查

Web应用中,各页面实际上是没有关系的,但为了给用户“完整”的感觉,需要用一个Session对象将用户访问的各页面“串”起来,同时,某些信息比较敏感,不希望被“窃听”或“修改”,因此,在每一页被访问时,通常的的做法是首先检查Session对象,以确定访问者的身份。

有许多种方法可以实现上面的逻辑,例如,数据可缓存在浏览器或服务器中,对敏感数据采用特别的加密解密方式等。本文采用cookie(id)+服务器端redis缓存session这样一种方式。这一方式的特点是所有数据保存在服务器端,可以不用加密,缺点是服务器端需要承受一定的负荷。

浏览器的cookie中,保存有一个服务器端随机生成的id,每个请求必须包含此id,服务器端根据此id搜索缓存,得出访问者身份及其他操作信息。如果此id不能被识别,UI页面的情况下,转到登陆页,成功后返回上面的UI页;数据页面的情况下,直接操作失败,返回错误页。

    在进入每一个受控页面前,都需要同样的处理逻辑,采用python的修饰符是最好的方式:只需在页面响应函数前加上修饰函数即可。

Tornado提供了这样一个修饰符:@tornado.web.authenticated,以下是官网的解决方案,

class BaseHandler(tornado.web.RequestHandler):
    def get_current_user(self):
        return self.get_secure_cookie("user")

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)
先写一个BaseHandler,所有的页面继承于它(实际上还有其他的原因,这里的示例是MainHandler)。在页面get方法被调用之前,authenticated装饰器确保current_usr属性有值,否则任何GET或HEAD请求都将重定向到应用设置中login_url指定的URL。此外,非法用户的POST请求将返回一个带有403(Forbidden)状态的HTTP响应。因此,authenticated装饰器依赖于current_user属性和login_url设置,前者在BaseHandler中定义,后者在application启动项里定义。

简单的应用,以上的逻辑基本够了,但想定制一些行为,BaseHandler 和@tornado.web.authenticated做得还不够,下面是一个比较实用的示例。

import tornado.web
import sys
#decorator of authentication
def WebPageAuthenticate(method):
    def wrapper(self, *args, **kwargs):
#use getcache to validate the user has benn logined
        if not self.get_cache():
            self.redirect("/login?redir="+self.request.path)
        return method(self, *args, **kwargs)
    return wrapper  
    
class BaseRequestHandler(tornado.web.RequestHandler):
    def __init__(self, application, request, **kwargs):
        super(BaseRequestHandler, self).__init__(application, request, **kwargs)
        self._sscache=None         #session caache
        self._ssid=None            #session id
    def get_session(self):  
        if not self._ssid:
            self._ssid = self.get_cookie("sessionid",None)
        return self._ssid
    def set_session(self, sessionid, withcookie=True):
        if withcookie:
            self._ssid = self.set_cookie("sessionid",sessionid)
        else:
            self._ssid=sessionid        
    def get_cache(self):
        if not self._sscache:
            sessionid = self.get_session()
            if sessionid:
                self._sscache = UserManager.GetCache(sessionid)   #这个函数读入特定用户的特定配置信息,在用户管理中定义(如从redis中读取)
            else:
               self._sscache =None
        return self._sscache
    def write_error(self, status_code, **kwargs):
        if False and self.settings.get("serve_traceback") and "exc_info" in kwargs:
# in debug mode, try to send a traceback
            super(BaseRequestHandler, self).write_error(status_code, **kwargs)
        else:
            if status_code == 404:
                msg=self.path_args[0]
                self.render("exception/404.ptm",source=msg)
            elif status_code == 500:
                if self.path_args:
                    msg=self.path_args[0]
                else :
                    msg = ""
                self.render('exception/500.ptm',source=msg)
            else:
                super(BaseRequestHandler, self).write_error(status_code, **kwargs)

class BaseStaticHandler(tornado.web.StaticFileHandler):
    def write_error(self, status_code, **kwargs):
        if False and self.settings.get("serve_traceback") and "exc_info" in kwargs:
# in debug mode, try to send a traceback
            super(BaseStaticHandler, self).write_error(status_code, **kwargs)
        else:
            if status_code == 404:
                msg=self.path_args[0]
                self.render("exception/404.ptm",source=msg)
            elif status_code == 500:
                if self.path_args:
                    msg=self.path_args[0]
                else :
                    msg = ""        
                self.render('exception/500.ptm',source="")
            else:
                super(BaseStaticHandler,self).write_error(status_code, **kwargs)
这里有一个修饰器函数,调用BaseRequestHandler中定义的get_cache,看看是否能得到缓存的该用户信息,如果能,则认为该用户登陆过,否则转到登陆页面(这里固定了”/login”),并且将当前页面记录到redir中,以便登陆成功后可以返回这个页。

BaseRequestHandler是所有python页面的基类,有三个功能:sessionid的获得与设置;用户缓存集合的获得;发生错误时页面的重定向。这里的sessionid不像官网的例子,无需加密,它仅是一个guid。如何获得用户缓存,可在别处定义,例如可与下面讨论的redis结合。BaseStaticHandler是静态请求处理类,可以定制化对静态文件的处理过程。

为方便,上面登陆页(”/login”)及错误处理页(exception/500.ptm等)都固定化了,也可以用define定义它们并在配置中进行配置。需要注意的是登陆页本身不能加修饰器,否则就循环了。

3.  全局变量

一般情况下,一个应用需要一些共享的信息,例如配置等。Python各模块之间共享信息,可以将信息统一到一个模块,然后各模块进行引用。Tornado中,定义了options模块,也可以利用它设置全局变量(options本来是用来定义web启动选项的,供tornado.options.parse_command_line进行解析)。

以下是一个例子来说明,如下:

import tornado.ioloop
import tornado.web
import tornado.options

class aclass:
    def __init__(self):
        self.count=5
    def inc(self,a):
        return self.count+a

myobject=aclass()
        
tornado.options.define("myobject", default=myobject, help="globle object")
tornado.options.define("number", default=10, type=int, help="A number")
tornado.options.define("port", default=8000, type=int, help="A number")
class MainHandler(tornado.web.RequestHandler):
    def get(self):
    tornado.options.options.myobject.count=tornado.options.options.myobject.inc(tornado.options.options.number)
        self.write("Hello, world:{}".format(tornado.options.options.myobject.count))
def runweb():
    webapp=tornado.web.Application([
        (r"/", MainHandler),
    ])
    
    webapp.listen(tornado.options.options.port)
    tornado.ioloop.IOLoop.current().start()
        
if __name__ == "__main__":
    runweb()
---------------------  
作者:guxch  
来源:CSDN  
原文:https://blog.csdn.net/guxch/article/details/80811775  
版权声明:本文为博主原创文章,转载请附上博文链接!

posted on 2018-11-02 18:41  myworldworld  阅读(508)  评论(0)    收藏  举报

导航