Tornado开发(四)— 扩展

2.  页面的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()

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值