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的一些用法分享
Oct 07 Python
Python3实现从指定路径查找文件的方法
May 22 Python
使用Python生成随机密码的示例分享
Feb 18 Python
python实现彩色图转换成灰度图
Jan 15 Python
Python pandas实现excel工作表合并功能详解
Aug 29 Python
Python下应用opencv 实现人脸检测功能
Oct 24 Python
Python OpenCV视频截取并保存实现代码
Nov 30 Python
pytorch GAN伪造手写体mnist数据集方式
Jan 10 Python
python中round函数如何使用
Jun 19 Python
pytorch判断是否cuda 判断变量类型方式
Jun 23 Python
pycharm 添加解释器的方法步骤
Aug 31 Python
python 两种方法修改文件的创建时间、修改时间、访问时间
Sep 26 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
ubuntu 编译安装php 5.3.3+memcache的方法
2010/08/05 PHP
php构造函数实例讲解
2013/11/13 PHP
php中get_cfg_var()和ini_get()的用法及区别
2015/03/04 PHP
详解WordPress中提醒安装插件以及隐藏插件的功能实现
2015/12/25 PHP
PHP 返回数组后处理方法(开户成功后弹窗提示)
2017/07/03 PHP
PHP查找一列有序数组是否包含某值的方法
2020/02/07 PHP
详解PHP中的8个魔术常量
2020/07/06 PHP
ie 调试javascript的工具
2009/04/29 Javascript
非阻塞动态加载javascript广告实现代码
2010/11/17 Javascript
javascript验证身份证完全方法具体实现
2013/11/18 Javascript
JavaScript使用HTML5的window.postMessage实现跨域通信例子
2014/04/11 Javascript
js点击button按钮跳转到另一个新页面
2014/10/10 Javascript
浅析JSONP技术原理及实现
2016/06/08 Javascript
jQuery复制节点用法示例(clone方法)
2016/09/08 Javascript
jQuery实现的两种简单弹窗效果示例
2018/04/18 jQuery
解决vue-quill-editor上传内容由于图片是base64的导致字符太长的问题
2018/08/20 Javascript
微信JS-SDK updateAppMessageShareData安卓不能自定义分享详解
2019/03/29 Javascript
javascript实现图片轮播代码
2019/07/09 Javascript
聊聊鉴权那些事(推荐)
2019/08/22 Javascript
[46:12]完美世界DOTA2联赛循环赛 DM vs Matador BO2第一场 11.04
2020/11/04 DOTA
在Python的while循环中使用else以及循环嵌套的用法
2015/10/14 Python
基于python 字符编码的理解
2017/09/02 Python
用Python将一个列表分割成小列表的实例讲解
2018/07/02 Python
Face++ API实现手势识别系统设计
2018/11/21 Python
解决 jupyter notebook 回车换两行问题
2020/04/15 Python
python函数调用,循环,列表复制实例
2020/05/03 Python
Python基础教程(一)——Windows搭建开发Python开发环境
2020/07/20 Python
一张图片能隐含千言万语之隐藏你的程序代码
2012/12/13 HTML / CSS
NICKIS.com荷兰:设计师儿童时装
2020/01/08 全球购物
农行实习自我鉴定
2013/09/22 职场文书
母校寄语大全
2014/04/10 职场文书
北京奥运会口号
2014/06/21 职场文书
党员干部廉政承诺书
2015/04/28 职场文书
工会文体活动总结
2015/05/07 职场文书
中国现代文学之经典散文三篇
2019/09/18 职场文书
如何理解及使用Python闭包
2021/06/01 Python