flask session组件的使用示例


Posted in Python onDecember 25, 2018

一、简介

flask中session组件可分为内置的session组件还有第三方flask-session组件,内置的session组件功能单一,而第三方的flask-sessoin可支持redis、memcached、文件等进行session的存储。以下将介绍内置session以及第三方session组件的使用方法以及处理机制。

二、内置session处理机制

Cookie与Session

Cookie:

Cookie意为“甜饼”,是由W3C组织提出,最早由Netscape社区发展的一种机制。目前Cookie已经成为标准,所有的主流浏览器如IE、Netscape、Firefox、Opera等都支持Cookie。由于HTTP是一种无状态的协议,服务器单从网络连接上无从知道客户身份。怎么办呢?就给客户端们颁发一个通行证吧,每人一个,无论谁访问都必须携带自己通行证。这样服务器就能从通行证上确认客户身份了。这就是Cookie的工作原理。

Cookie实际上是一小段的文本信息。客户端请求服务器,如果服务器需要记录该用户状态,就使用response向客户端浏览器颁发一个Cookie。客户端浏览器会把Cookie保存起来。当浏览器再请求该网站时,浏览器把请求的网址连同该Cookie一同提交给服务器。服务器检查该Cookie,以此来辨认用户状态。服务器还可以根据需要修改Cookie的内容

Session:

Session是另一种记录客户状态的机制,不同的是Cookie保存在客户端浏览器中,而Session保存在服务器上。客户端浏览器访问服务器的时候,服务器把客户端信息以某种形式记录在服务器上。这就是Session。客户端浏览器再次访问时只需要从该Session中查找该客户的状态就可以了,实质上session就是保存在服务器端的键值对。

如果说Cookie机制是通过检查客户身上的“通行证”来确定客户身份的话,那么Session机制就是通过检查服务器上的“客户明细表”来确认客户身份。Session相当于程序在服务器上建立的一份客户档案,客户来访的时候只需要查询客户档案表就可以了。

第一次请求,session的创建过程

在flask上下文中介绍了,请求到flask框架会执行wsgi_app方法:

def wsgi_app(self, environ, start_response):
 ctx = self.request_context(environ) #实例化生成RequestContext对象
 error = None 
 try:
  try:
   ctx.push() #push上下文到LocalStack中
   response = self.full_dispatch_request() #执行视图函数过程
  except Exception as e:
   error = e
   response = self.handle_exception(e) #处理异常
  except:
   error = sys.exc_info()[1]
   raise
  return response(environ, start_response)
 finally:
  if self.should_ignore_error(error):
   error = None
  ctx.auto_pop(error)  # 删除LocalStack中的数据

在改方法中会生成一个ctx也就是RequestContext对象:

class RequestContext(object):
 def __init__(self, app, environ, request=None):
  self.app = app # app对象
  if request is None:
   request = app.request_class(environ) 
  self.request = request # 封装request
  self.url_adapter = app.create_url_adapter(self.request)
  self.flashes = None
  self.session = None #一开始的session

在这个对象中封装了session,最初为None。接着在wsgi_app中执行ctx.push:

def push(self):
  app_ctx = _app_ctx_stack.top # 获取app上下文
  if app_ctx is None or app_ctx.app != self.app:
   app_ctx = self.app.app_context() #将app上下文push到app_ctx对于的LocalStack中
   app_ctx.push()
   self._implicit_app_ctx_stack.append(app_ctx)
  else:
   self._implicit_app_ctx_stack.append(None)
  if hasattr(sys, 'exc_clear'):
   sys.exc_clear()
  _request_ctx_stack.push(self)
  if self.session is None: # 判断session是否为None,一开始为None
   session_interface = self.app.session_interface # 获取操作session的对象
   self.session = session_interface.open_session( #调用open_session 创建session
    self.app, self.request
   )
   if self.session is None:
    self.session = session_interface.make_null_session(self.app)

这里我们主要关注session,前面的代码在上下文中已经进行了相关说明,这里有个判断session是否为None,刚开始RequestContext中的session为None,所以条件成立,此时执行以下语句:

session_interface = self.app.session_interface
self.session = session_interface.open_session(
    self.app, self.request
   )
if self.session is None:
 self.session = session_interface.make_null_session(self.app)

首先来看session_interface = self.app.session_interface,self.app.session_interface就是app中的session_interface属性:

session_interface = SecureCookieSessionInterface()

默认是一个SecureCookieSessionInterface()对象,该对象的内部主要实现了open_session和save_session用于使用和保存session。接着self.session被重新赋值为session_interface.open_session(self.app, self.request)方法返回的值,以下为open_session源码:

def open_session(self, app, request):
 s = self.get_signing_serializer(app) #根据app.secret_key获取签名算法
 if s is None:
  return None
 # 根据配置中的session_cookie_name获取session对于的值
 val = request.cookies.get(app.session_cookie_name) #如果request.cookies为空,val为空
 if not val:
  return self.session_class()
 max_age = total_seconds(app.permanent_session_lifetime)    
 try:
  data = s.loads(val, max_age=max_age)
  return self.session_class(data)
 except BadSignature:
  return self.session_class()

该方法返回self.session_class(),当请求第一次来时,request.cookies为None,所以val也为None,返回self.session_class(),而session_class又是SecureCookieSession:

session_class = SecureCookieSession

所以我们继续看SecureCookieSession:

class SecureCookieSession(CallbackDict, SessionMixin):
 accessed = False

 def __init__(self, initial=None):
  def on_update(self):
   self.modified = True
   self.accessed = True

  super(SecureCookieSession, self).__init__(initial, on_update)

 def __getitem__(self, key):
  self.accessed = True
  return super(SecureCookieSession, self).__getitem__(key)

 def get(self, key, default=None):
  self.accessed = True
  return super(SecureCookieSession, self).get(key, default)

 def setdefault(self, key, default=None):
  self.accessed = True
  return super(SecureCookieSession, self).setdefault(key, default)

该类继承了CallbackDict, SessionMixin我们继续来看看CallbackDict:

class CallbackDict(UpdateDictMixin, dict):

 """A dict that calls a function passed every time something is changed.
 The function is passed the dict instance.
 """

 def __init__(self, initial=None, on_update=None):
  dict.__init__(self, initial or ())
  self.on_update = on_update

 def __repr__(self):
  return '<%s %s>' % (
   self.__class__.__name__,
   dict.__repr__(self)
  )

也就是说SecureCookieSession继承了CallbackDict而CallbackDict继承了原生的dict,所以我们可以认为SecureCookieSession是一个特殊的字典,是调用了SecureCookieSessionInterface类中open_session返回的特殊字典,经过进一步分析self.session此时就是这个字典,这也意味着session在执行open_session方法时候被创建了,并保存在ctx中,也就是在RequestContext对象中,当我们使用session时候是通过全局变量session = LocalProxy(partial(_lookup_req_object, 'session'))由LocalProxy对象从ctx中获取到session。

第二次请求

开始我们知道session第一次请求来的时候是在open_session方法之后被创建,当第二次请求时,此时在open_session方法中,val已经不在是None,此时获取cookie的有效时长,如果cookie依然有效,通过与写入时同样的签名算法将cookie中的值解密出来并写入字典并返回,若cookie已经失效,则仍然返回'空字典',这样以来在第二次请求中就能获取到之前保存的session数据。

session生命周期

我们介绍了session创建时候是在ctx.push时候开始创建,也就是说在这之后我们就可以使用session,对它进行操作了,那么session什么时候保存呢?我们接下来继续看wsgi_app:

def wsgi_app(self, environ, start_response):
 ctx = self.request_context(environ)
 error = None
 try:
  try:
   ctx.push()
   response = self.full_dispatch_request()
  except Exception as e:
   error = e
   response = self.handle_exception(e)
  except:
   error = sys.exc_info()[1]
   raise
  return response(environ, start_response)
 finally:
  if self.should_ignore_error(error):
   error = None
  ctx.auto_pop(error)

生成session后,接着执行self.full_dispatch_request():

def full_dispatch_request(self):
 """Dispatches the request and on top of that performs request
 pre and postprocessing as well as HTTP exception catching and
 error handling.

 .. versionadded:: 0.7
 """
 self.try_trigger_before_first_request_functions() #执行app.before_first_reques钩子函数
 try:
  request_started.send(self) # 触发request_started信号
  rv = self.preprocess_request() # 执行before_request钩子函数
  if rv is None:
   rv = self.dispatch_request() # 执行视图函数
 except Exception as e:
  rv = self.handle_user_exception(e)
 return self.finalize_request(rv)

这一部分先执行钩子app.before_first_reques在触发request_started信号,再执行before_request钩子函数,然后在执行视图函数,rv是执行完视图函数的返回值,最后执行finalize_request,这里的session保存就发生在这里:

def finalize_request(self, rv, from_error_handler=False):
 response = self.make_response(rv)
 try:
  response = self.process_response(response)
  request_finished.send(self, response=response)
 except Exception:
  if not from_error_handler:
   raise
  self.logger.exception('Request finalizing failed with an '
        'error while handling an error')
 return response

注意这里的在最后会session判断是否为空,会执行save_session方法,也就是SecureCookieSessionInterface的save_session方法:

def save_session(self, app, session, response):
 domain = self.get_cookie_domain(app)
 path = self.get_cookie_path(app)

 # If the session is modified to be empty, remove the cookie.
 # If the session is empty, return without setting the cookie.
 if not session:
  if session.modified:
   response.delete_cookie(
    app.session_cookie_name,
    domain=domain,
    path=path
   )

  return

 # Add a "Vary: Cookie" header if the session was accessed at all.
 if session.accessed:
  response.vary.add('Cookie')

 if not self.should_set_cookie(app, session):
  return

 httponly = self.get_cookie_httponly(app)
 secure = self.get_cookie_secure(app)
 samesite = self.get_cookie_samesite(app)
 expires = self.get_expiration_time(app, session)
 val = self.get_signing_serializer(app).dumps(dict(session))
 response.set_cookie(
  app.session_cookie_name,
  val,
  expires=expires,
  httponly=httponly,
  domain=domain,
  path=path,
  secure=secure,
  samesite=samesite
 )

该方法最后保存的session调用的response.set_cookie,其实是将数据保存在cookie中,也就是在客户端的浏览器中,并非在服务端进行数据的保存,当请求完毕后会执行ctx.auto_pop(error)这时候会从上下文中将session和request删除,到此,session的生命周期结束。

视图函数使用session

在介绍flask的上下文中就已经对session进行过介绍,其本质也是通过LocalProxy操作上下文从而设置session,我们以session['username']='wd'作为列子,首先根据

session = LocalProxy(partial(_lookup_req_object, 'session'))

session是一个LocalProxy对象,执行session['username']=‘wd'则执行LocalProxy对象的__setitem__方法,而__setitem__方法中则是调用_get_current_object获取ctx中的session对象,而其对象本质是一个特殊的字典,相当于在字典中加一对key,value。

小结

flask内置session本质上依靠上下文,当请求到来时,调用session_interface中的open_session方法解密获取session的字典,并保存在RequestContext.session中,也就是上下文中,然后在视图函数执行完毕后调用session_interface的save_session方法,将session以加密的方式写入response的cookie中,浏览器再保存数据。而第三方的session组件原理就是基于是open_session方法和save方法,从而实现session更多的session保存方案。

三、第三方组件flask-session

flask-session支持多种数据库session保存方案如:redis、memchached、mongodb甚至文件系统等。官方文档: https://pythonhosted.org/Flask-Session/

安装:

pip3 install flask-session

redis

import redis
from flask import Flask, session
from flask_session import Session
from datetime import timedelta

app = Flask(__name__)
app.debug = True
app.secret_key = 'adavafa'



app.config['SESSION_TYPE'] = 'redis' # session类型为redis
app.config['SESSION_PERMANENT'] = True # 如果设置为True,则关闭浏览器session就失效。
app.config['PERMANENT_SESSION_LIFETIME']=timedelta(seconds=20)
#一个持久化的会话的生存时间,是一个datetime.timedelta对象,也可以用一个整数来表示秒,前提设置了PERMANENT_SESSION_LIFETIME为True
app.config['SESSION_USE_SIGNER'] = False # 是否对发送到浏览器上session的cookie值进行加密,默认False 
app.config['SESSION_KEY_PREFIX'] = 'flask-session' # 保存到redis中的key的前缀
app.config['SESSION_COOKIE_NAME']= 'session_id'  # 保存在浏览器的cookie名称
app.config['SESSION_REDIS'] = redis.Redis(host='10.1.210.33', port=‘6379',password=‘123123') # 用于连接redis的配置


#其他配置,不经常使用
app.config['SESSION_COOKIE_DOMAIN']='127.0.0.1' # 设置cookie的域名,不建议设置默认为server_name
app.config['SESSION_COOKIE_PATH']='/' # 会话cookie的路径。 如果未设置,则cookie将对所有url有效,默认为'/'
app.config['SESSION_COOKIE_HTTPONLY']=True # 是否启动httponly,默认为true,为了防止xss脚本访问cookie

Session(app)
@app.route('/login')
def index():
 session["username"]="jack"
 return 'login'


if __name__ == '__main__':
 app.run()

Memchached

import memcache
from flask import Flask, session
from flask_session import Session
from datetime import timedelta

app = Flask(__name__)
app.debug = True
app.secret_key = 'adavafa'



app.config['SESSION_TYPE'] = ‘memcached' # session类型为memcached 
app.config['SESSION_PERMANENT'] = True # 如果设置为True,则关闭浏览器session就失效。
app.config['PERMANENT_SESSION_LIFETIME']=timedelta(seconds=20)
#一个持久化的会话的生存时间,是一个datetime.timedelta对象,也可以用一个整数来表示秒,前提设置了PERMANENT_SESSION_LIFETIME为True
app.config['SESSION_USE_SIGNER'] = False # 是否对发送到浏览器上session的cookie值进行加密,默认False
app.config['SESSION_KEY_PREFIX'] = 'flask-session' # 保存到缓存中的key的前缀
app.config['SESSION_COOKIE_NAME']= 'session_id'  # 保存在浏览器的cookie名称
app.config['SESSION_MEMCACHED'] = memcache.Client(['10.1.210.33:12000']) #连接


#其他配置,不经常使用
app.config['SESSION_COOKIE_DOMAIN']='127.0.0.1' # 设置cookie的域名,不建议设置默认为server_name
app.config['SESSION_COOKIE_PATH']='/' # 会话cookie的路径。 如果未设置,则cookie将对所有url有效,默认为'/'
app.config['SESSION_COOKIE_HTTPONLY']=True # 是否启动httponly,默认为true,为了防止xss脚本访问cookie




Session(app)


@app.route('/login')
def index():
 session["username"]="jack"
 return 'login'


if __name__ == '__main__':
 app.run()

Filesystem

from flask import Flask, session
from flask_session import Session
from datetime import timedelta

app = Flask(__name__)
app.debug = True
app.secret_key = 'adavafa'



app.config['SESSION_TYPE'] = 'filesystem' # session类型为filesystem
app.config['SESSION_FILE_DIR']='/opt/db' #文件保存目录
app.config['SESSION_FILE_THRESHOLD'] = 300 # 存储session的个数如果大于这个值时,开始删除

app.config['SESSION_PERMANENT'] = True # 如果设置为True,则关闭浏览器session就失效。
app.config['PERMANENT_SESSION_LIFETIME']=timedelta(seconds=20)
#一个持久化的会话的生存时间,是一个datetime.timedelta对象,也可以用一个整数来表示秒,前提设置了PERMANENT_SESSION_LIFETIME为True
app.config['SESSION_USE_SIGNER'] = False # 是否对发送到浏览器上session的cookie值进行加密,默认False
app.config['SESSION_KEY_PREFIX'] = 'flask-session' # 保存到文件中的key的前缀
app.config['SESSION_COOKIE_NAME']= 'session_id'  # 保存在浏览器的cookie名称


#其他配置,不经常使用
app.config['SESSION_COOKIE_DOMAIN']='127.0.0.1' # 设置cookie的域名,不建议设置默认为server_name
app.config['SESSION_COOKIE_PATH']='/' # 会话cookie的路径。 如果未设置,则cookie将对所有url有效,默认为'/'
app.config['SESSION_COOKIE_HTTPONLY']=True # 是否启动httponly,默认为true,为了防止xss脚本访问cookie

Session(app)


@app.route('/login')
def index():
 session["username"]="jack"
 return 'login'


if __name__ == '__main__':
 app.run()

mongodb

import pymongo
from flask import Flask, session
from flask_session import Session
from datetime import timedelta

app = Flask(__name__)
app.debug = True
app.secret_key = 'adavafa'


app.config['SESSION_TYPE'] = 'mongodb' # session类型为mongodb
app.config['SESSION_MONGODB'] = pymongo.MongoClient('localhost',27017)
app.config['SESSION_MONGODB_DB'] = '数据库名称'
app.config['SESSION_MONGODB_COLLECT'] = '表名称'


app.config['SESSION_PERMANENT'] = True # 如果设置为True,则关闭浏览器session就失效。
app.config['PERMANENT_SESSION_LIFETIME']=timedelta(seconds=20)
#一个持久化的会话的生存时间,是一个datetime.timedelta对象,也可以用一个整数来表示秒,前提设置了PERMANENT_SESSION_LIFETIME为True
app.config['SESSION_USE_SIGNER'] = False # 是否对发送到浏览器上session的cookie值进行加密,默认False
app.config['SESSION_KEY_PREFIX'] = 'flask-session' # 保存的session的key的前缀
app.config['SESSION_COOKIE_NAME']= 'session_id'  # 保存在浏览器的cookie名称


#其他配置,不经常使用
app.config['SESSION_COOKIE_DOMAIN']='127.0.0.1' # 设置cookie的域名,不建议设置默认为server_name
app.config['SESSION_COOKIE_PATH']='/' # 会话cookie的路径。 如果未设置,则cookie将对所有url有效,默认为'/'
app.config['SESSION_COOKIE_HTTPONLY']=True # 是否启动httponly,默认为true,为了防止xss脚本访问cookie

Session(app)


@app.route('/login')
def index():
 session["username"]="jack"
 return 'login'


if __name__ == '__main__':
 app.run()

sqlalchemy

import redis
from flask import Flask, session
from flask_session import Session 
from flask_sqlalchemy import SQLAlchemy

app = Flask(__name__)
app.debug = True
app.secret_key = 'adavafa'

# 设置数据库链接
app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql+pymysql://root:dev@127.0.0.1:3306/devops?charset=utf8'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = True
# 实例化SQLAlchemy
db = SQLAlchemy(app)
app.config['SESSION_TYPE'] = 'sqlalchemy' # session类型为sqlalchemy
app.config['SESSION_SQLALCHEMY'] = db # SQLAlchemy对象
app.config['SESSION_SQLALCHEMY_TABLE'] = '表名' # session要保存的表名称

app.config['SESSION_PERMANENT'] = True # 如果设置为True,则关闭浏览器session就失效。
app.config['PERMANENT_SESSION_LIFETIME']=timedelta(seconds=20)
#一个持久化的会话的生存时间,是一个datetime.timedelta对象,也可以用一个整数来表示秒,前提设置了PERMANENT_SESSION_LIFETIME为True
app.config['SESSION_USE_SIGNER'] = False # 是否对发送到浏览器上session的cookie值进行加密,默认False
app.config['SESSION_KEY_PREFIX'] = 'flask-session' # 保存的session的key的前缀
app.config['SESSION_COOKIE_NAME']= 'session_id'  # 保存在浏览器的cookie名称


#其他配置,不经常使用
app.config['SESSION_COOKIE_DOMAIN']='127.0.0.1' # 设置cookie的域名,不建议设置默认为server_name
app.config['SESSION_COOKIE_PATH']='/' # 会话cookie的路径。 如果未设置,则cookie将对所有url有效,默认为'/'
app.config['SESSION_COOKIE_HTTPONLY']=True # 是否启动httponly,默认为true,为了防止xss脚本访问cookie




Session(app)


@app.route('/login')
def index():
 session["username"]="jack"
 return 'login'


if __name__ == '__main__':
 app.run()


###使用SQLAlchemy时候先确保数据库和表都存在
在命令行中创建表
#>>> from app import db
#>>> db.create_all()

原理

这里以redis作为session存储方案做分析,以下是RedisSessionInterface源码:

class RedisSessionInterface(SessionInterface):
 serializer = pickle
 session_class = RedisSession

 def __init__(self, redis, key_prefix, use_signer=False, permanent=True):
  if redis is None:
   from redis import Redis
   redis = Redis()
  self.redis = redis
  self.key_prefix = key_prefix
  self.use_signer = use_signer
  self.permanent = permanent

 def open_session(self, app, request):
  sid = request.cookies.get(app.session_cookie_name)
  if not sid:
   sid = self._generate_sid()
   return self.session_class(sid=sid, permanent=self.permanent)
  if self.use_signer:
   signer = self._get_signer(app)
   if signer is None:
    return None
   try:
    sid_as_bytes = signer.unsign(sid)
    sid = sid_as_bytes.decode()
   except BadSignature:
    sid = self._generate_sid()
    return self.session_class(sid=sid, permanent=self.permanent)

  if not PY2 and not isinstance(sid, text_type):
   sid = sid.decode('utf-8', 'strict')
  val = self.redis.get(self.key_prefix + sid)
  if val is not None:
   try:
    data = self.serializer.loads(val)
    return self.session_class(data, sid=sid)
   except:
    return self.session_class(sid=sid, permanent=self.permanent)
  return self.session_class(sid=sid, permanent=self.permanent)

 def save_session(self, app, session, response):
  domain = self.get_cookie_domain(app)
  path = self.get_cookie_path(app)
  if not session:
   if session.modified:
    self.redis.delete(self.key_prefix + session.sid)
    response.delete_cookie(app.session_cookie_name,
          domain=domain, path=path)
   return
  httponly = self.get_cookie_httponly(app)
  secure = self.get_cookie_secure(app)
  expires = self.get_expiration_time(app, session)
  val = self.serializer.dumps(dict(session))
  self.redis.setex(name=self.key_prefix + session.sid, value=val,
       time=total_seconds(app.permanent_session_lifetime))
  if self.use_signer:
   session_id = self._get_signer(app).sign(want_bytes(session.sid))
  else:
   session_id = session.sid
  response.set_cookie(app.session_cookie_name, session_id,
       expires=expires, httponly=httponly,
       domain=domain, path=path, secure=secure)

分析:RedisSessionInterface继承了SessionInterface

class SessionInterface(FlaskSessionInterface):

 def _generate_sid(self):
  return str(uuid4())

 def _get_signer(self, app):
  if not app.secret_key:
   return None
  return Signer(app.secret_key, salt='flask-session',
      key_derivation='hmac')

而SessionInterface又继承了FlaskSessionInterface,而FlaskSessionInterface又继承了flask内置的SessionInterface,并且RedisSessionInterface重写了内置session的open_session和save_session.

首先是RedisSessionInterface实例化用于初始化配置,例如redis的连接、签名配置、过期配置、前缀配置等。

接下来看两个核心方法:open_session方法和save_session方法。

open_session:

def open_session(self, app, request):
 sid = request.cookies.get(app.session_cookie_name) #获取session id
 if not sid: #判断session id是否为空,为空表示第一次请求
  sid = self._generate_sid() # 返回使用uuid4随机字符串
  return self.session_class(sid=sid, permanent=self.permanent)
 if self.use_signer:# 判断签名配置
  signer = self._get_signer(app)
  if signer is None:
   return None
  try:
   sid_as_bytes = signer.unsign(sid) # 对session id 进行加密签名
   sid = sid_as_bytes.decode()
  except BadSignature:
   sid = self._generate_sid()
   return self.session_class(sid=sid, permanent=self.permanent)

 if not PY2 and not isinstance(sid, text_type):
  sid = sid.decode('utf-8', 'strict')
 val = self.redis.get(self.key_prefix + sid) # 获取seession数据
 if val is not None:
  try:
   data = self.serializer.loads(val) # 反序列化数据
   return self.session_class(data, sid=sid) #返回
  except:
   return self.session_class(sid=sid, permanent=self.permanent)
 return self.session_class(sid=sid, permanent=self.permanent)

改方法先从cookie中获取session id,然后对session id判断是否为空,为空表示第一次请求,则通过self._generate_sid()返回随机字符串,作为返回给浏览器的sessionid

def _generate_sid(self):
 return str(uuid4())

接着判断签名判断是否为true,然后对session 进行签名,这里和内置session不同的是获取session的时候通过self.redis.get(self.key_prefix + sid)在redis中进行获取。

save_session:

def save_session(self, app, session, response):
 domain = self.get_cookie_domain(app) # 获取cookie中的域名
 path = self.get_cookie_path(app) # 获取cookie 中path
 if not session: # 判断有误session对象
  if session.modified: #没有但是被修改了,表示已经被删除了
   self.redis.delete(self.key_prefix + session.sid) #清空session
   response.delete_cookie(app.session_cookie_name,
         domain=domain, path=path)
  return

 httponly = self.get_cookie_httponly(app)
 secure = self.get_cookie_secure(app)
 expires = self.get_expiration_time(app, session)
 val = self.serializer.dumps(dict(session))
 self.redis.setex(name=self.key_prefix + session.sid, value=val,
      time=total_seconds(app.permanent_session_lifetime)) #保存session
 if self.use_signer:
  session_id = self._get_signer(app).sign(want_bytes(session.sid))
 else:
  session_id = session.sid
 response.set_cookie(app.session_cookie_name, session_id, # 设置cookie
      expires=expires, httponly=httponly,
      domain=domain, path=path, secure=secure)

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Python 相关文章推荐
Python深入学习之装饰器
Aug 31 Python
Python中使用第三方库xlutils来追加写入Excel文件示例
Apr 05 Python
详解python eval函数的妙用
Nov 16 Python
python正则实现提取电话功能
Feb 24 Python
python3 面向对象__类的内置属性与方法的实例代码
Nov 09 Python
PyQt打开保存对话框的方法和使用详解
Feb 27 Python
Python的高阶函数用法实例分析
Apr 11 Python
Tensorflow 1.0之后模型文件、权重数值的读取方式
Feb 12 Python
浅谈Python中os模块及shutil模块的常规操作
Apr 03 Python
pyspark给dataframe增加新的一列的实现示例
Apr 24 Python
Python读取pdf表格写入excel的方法
Jan 22 Python
用Python简陋模拟n阶魔方
Apr 17 Python
python+opencv实现霍夫变换检测直线
Oct 23 #Python
python利用插值法对折线进行平滑曲线处理
Dec 25 #Python
基于Python对数据shape的常见操作详解
Dec 25 #Python
Python正则匹配判断手机号是否合法的方法
Dec 09 #Python
对Python正则匹配IP、Url、Mail的方法详解
Dec 25 #Python
Python 正则表达式匹配字符串中的http链接方法
Dec 25 #Python
Python lambda表达式用法实例分析
Dec 25 #Python
You might like
MYSQL环境变量设置方法
2007/01/15 PHP
PHP开发规范手册之PHP代码规范详解
2011/01/13 PHP
php 魔术常量详解及实例代码
2016/12/04 PHP
windows下的WAMP环境搭建图文教程(推荐)
2017/07/27 PHP
PHP实现图片防盗链破解操作示例【解决图片防盗链问题/反向代理】
2020/05/29 PHP
使用CSS3实现字体颜色渐变的实现
2021/03/09 HTML / CSS
关于B/S判断浏览器断开的问题讨论
2008/10/29 Javascript
js实现上传图片之上传前预览图片
2013/03/25 Javascript
js jquery ajax的几种用法总结(及优缺点介绍)
2014/01/28 Javascript
jquery 中的each()跳出循环的语句
2014/05/23 Javascript
深入分析原生JavaScript事件
2014/12/29 Javascript
限制上传文件大小和格式的jQuery插件实例
2015/01/24 Javascript
JavaScript前补零操作实例
2015/03/11 Javascript
jQuery实现的网页换肤效果示例
2016/09/20 Javascript
关于js二维数组和多维数组的定义声明(详解)
2016/10/02 Javascript
bootstrap监听滚动实现头部跟随滚动
2016/11/08 Javascript
jquery Easyui Datagrid实现批量操作(编辑,删除,添加)
2017/02/20 Javascript
JS简单验证上传文件类型的方法
2017/04/17 Javascript
从零开始最小实现react服务器渲染详解
2018/01/26 Javascript
JS实现移动端可折叠导航菜单(现代都市风)
2020/07/07 Javascript
Node.JS如何实现JWT原理
2020/09/18 Javascript
[42:24]完美世界DOTA2联赛PWL S2 LBZS vs FTD.C 第三场 11.27
2020/12/01 DOTA
python实现xlsx文件分析详解
2018/01/02 Python
详解python--模拟轮盘抽奖游戏
2019/04/12 Python
pandas 数据结构之Series的使用方法
2019/06/21 Python
Python数据分析库pandas高级接口dt的使用详解
2020/12/11 Python
python多线程和多进程关系详解
2020/12/14 Python
web页面录屏实现
2019/02/12 HTML / CSS
New Balance德国官方网站:购买鞋子和服装
2019/08/31 全球购物
澳大利亚手袋、珠宝和在线时尚精品店:The Way
2019/12/21 全球购物
shell程序如何生命变量?shell变量是弱变量吗?
2014/11/10 面试题
骨干教师培训感言
2014/01/16 职场文书
职务说明书范文
2014/05/07 职场文书
2014年局领导班子自身建设情况汇报
2014/11/21 职场文书
《妈妈别哭,有我在》读后感3篇
2020/01/13 职场文书
微信小程序APP的事件绑定以及传递参数时的冒泡和捕获
2022/04/19 Javascript