Tornado的cookie过期问题
首先,web应用程序是使用HTTP协议进行数据传输,因为HTTP协议是无状态的,所以一旦提交数据完成后,客户端和服务器端的连接就会被关闭,再次进行数据的交换就得重新建立新的连接,那么,有个问题就是服务器无法通过连接来跟踪用户的会话。接下来,对于Session和Cookie这个技术就出来了。
简单介绍一下Session和Cookie:
Session:通过在服务器端记录用户信息从而来确认用户身份,保存在服务器上,每个用户会话都有一个对应的session
Cookie:通过在客户端记录信息确认身份,客户端浏览器会把Cookie保存起来,当再次访问的时候,把该Cookie一同提交给服务器,就可以进行用户的辨认了
工作时,需要对Tornado的Cookie设置过期时间,然后对于Tornado来说,查看源码得知对Cookie的处理有4个方法。
下面给出4个方法的源码:
版本说明: Tornado版本为4.4.2
a. get_cookie()
1 2 3 4 5 | def get_cookie( self , name, default = None ): """Gets the value of the cookie with the given name, else default.""" if self .request.cookies is not None and name in self .request.cookies: return self .request.cookies[name].value return default |
可以从源码注释中容易的明白,其为获取cookie的方法,通过给予参数name后拿到cookie的值
b. set_cookie()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | def set_cookie( self , name, value, domain = None , expires = None , path = "/" , expires_days = None , * * kwargs): """Sets the given cookie name/value with the given options. Additional keyword arguments are set on the Cookie.Morsel directly. See http://docs.python.org/library/cookie.html#morsel-objects for available attributes. """ # The cookie library only accepts type str, in both python 2 and 3 name = escape.native_str(name) value = escape.native_str(value) if re.search(r "[\x00-\x20]" , name + value): # Don't let us accidentally inject bad stuff raise ValueError( "Invalid cookie %r: %r" % (name, value)) if not hasattr ( self , "_new_cookie" ): self ._new_cookie = Cookie.SimpleCookie() if name in self ._new_cookie: del self ._new_cookie[name] self ._new_cookie[name] = value morsel = self ._new_cookie[name] if domain: morsel[ "domain" ] = domain if expires_days is not None and not expires: expires = datetime.datetime.utcnow() + datetime.timedelta( days = expires_days) if expires: morsel[ "expires" ] = httputil.format_timestamp(expires) if path: morsel[ "path" ] = path for k, v in kwargs.items(): if k = = 'max_age' : k = 'max-age' # skip falsy values for httponly and secure flags because # SimpleCookie sets them regardless if k in [ 'httponly' , 'secure' ] and not v: continue morsel[k] = v |
这个是对Cookie的设置方法,看到expires和expires_days似乎就是我们所要用到的过期时间的设置项
c. get_secure_cookie()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | def get_secure_cookie( self , name, value = None , max_age_days = 31 , min_version = None ): """Returns the given signed cookie if it validates, or None. The decoded cookie value is returned as a byte string (unlike `get_cookie`). .. versionchanged:: 3.2.1 Added the ``min_version`` argument. Introduced cookie version 2; both versions 1 and 2 are accepted by default. """ self .require_setting( "cookie_secret" , "secure cookies" ) if value is None : value = self .get_cookie(name) return decode_signed_value( self .application.settings[ "cookie_secret" ], name, value, max_age_days = max_age_days, min_version = min_version) |
这个get_secure_cookie()也是获取cookie的方法,但是和上面的get_cookie()不同在于这个cookie值是一个加密的字符串,通过加密传输,但是get_cookie()是明文进行传输的,然后还注意到有一个参数 max_age_day=31,这个可以结合下面set_secure_cookie进行理解
d. set_secure_cookie()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | def set_secure_cookie( self , name, value, expires_days = 30 , version = None , * * kwargs): """Signs and timestamps a cookie so it cannot be forged. You must specify the ``cookie_secret`` setting in your Application to use this method. It should be a long, random sequence of bytes to be used as the HMAC secret for the signature. To read a cookie set with this method, use `get_secure_cookie()`. Note that the ``expires_days`` parameter sets the lifetime of the cookie in the browser, but is independent of the ``max_age_days`` parameter to `get_secure_cookie`. Secure cookies may contain arbitrary byte values, not just unicode strings (unlike regular cookies) .. versionchanged:: 3.2.1 Added the ``version`` argument. Introduced cookie version 2 and made it the default. """ self .set_cookie(name, self .create_signed_value(name, value, version = version), expires_days = expires_days, * * kwargs) |
通过查看该方法,我们可以发现,其实该方法就是对第一个set_cookie的进一步封装,通过注释可以知道:要使用这个方法对cookie进行设置,必须在项目的Application中,定义一个“cookie_secret” --> 这个cookie_secret应该是随机的,并且长字符串,当然你可以设置成你自己定义的固定的字符串。然后还有一个参数expires_days特别需要值得注意,这里如果我们不进行设置的话,cookie的过期时间将为30天,当然,这个跟get_secure_cookie中的max_age_day是相互独立的。
接下来,我们通过源码分析后,我们想对cookie设置过期时间,只需要在set_secure_cookie()函数中,将expires_day进行重写就可以了。
这里需要注意一下几点,是我在设置过程中踩过的坑,记录下来:
1.这个设置过期时间的参数 expires_day,如果想以秒作为过期单位的话,应该这样写:expires_day = time.time() + 对应的秒数
2.如果想设置,关闭浏览器就清楚cookie的话,应该这样设置,将过期时间重写置空:expires_day = None
3.这个问题,也是我遇到最蛋疼的问题,当时,我设置后,设置关闭浏览器清楚cookie一直无法失效,还一度怀疑是tornado对于chorme浏览器的bug,之后发现了解决办法,请看图:
就是因为chrome这个设置选项,默认是勾选了“关闭浏览器后继续运行后台应用”,所以我每次关闭浏览器打开后cookie永远不会失效。(Ubuntu系统,不同ps去查看系统进程的话根本发现不了没有关闭chrome的进程)
最后总结一下,Session和Cookie在整个项目中具体的设置和开发方法:
两种状态:
1.对于登陆是会话级别的,就是在系统上面一直有操作的,可以自定义cookie的过期时间
2.对于客户端来说,选择关闭浏览器就清楚cookie
开发方法:
1.对于登陆是会话级别,当用户一直有操作的时候,我们不能让cookie直接过期,所以我们在调用set_secure_cookie时,应该设置expires_day=None(关闭浏览器直接过期,如果expires_day后面有过期时间的话,会导致用户在操作途中直接cookie过期而要求重新登陆);
2.然后,我们如何保持用户在没有操作的一段时间后进行过期呢? 这就要看我们的记录在服务器端的session了,一般我们会把服务器端的session存储在memcache、redis等缓存服务器中,然后设置相应的缓存过期时间,每次当用户在系统上面有操作,有请求发生的时候,我们就重写缓存,重置session的过期时间,当用户一段时间没有操作的时候,存储在缓存中的session就会过期被清除,当用户再次发送请求后发现无法找到对应的session,从而让用户无法进行相应的操作客户端会提示重新登陆。这样子,就可以实现当用户有操作就不过期,没有操作就在一定的时间断内过期要求用户重新登陆了。
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步