一、flask session简介

flask中session组件可分为内置的session组件还有第三方flask-session组件,内置的session组件缺点:

  • 功能单一

  • session是保存在浏览器中的cookie中,不安全,

  • 大小有限制

而第三方插件flask-session可支持redis、memcached、文本等session的存储。

二、内置session处理机制

Cookie与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流程

1.第一次请求,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。

2.第二次请求

开始我们知道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='') # 用于连接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)

Flask框架【七】—session组件详解的更多相关文章

  1. flask基础之session原理详解(十)

    前言 flask_session是flask框架实现session功能的一个插件,用来替代flask自带的session实现机制,flask默认的session信息保存在cookie中,不够安全和灵活 ...

  2. Angular6 学习笔记——组件详解之组件通讯

    angular6.x系列的学习笔记记录,仍在不断完善中,学习地址: https://www.angular.cn/guide/template-syntax http://www.ngfans.net ...

  3. ISO七层模型详解

    ISO七层模型详解 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 在我刚刚接触运维这个行业的时候,去面试时总是会做一些面试题,笔试题就是看一个运维工程师的专业技能的掌握情况,这个很 ...

  4. Tomcat系列之服务器的安装与配置以及各组件详解

    Tomcat系列之服务器的安装与配置以及各组件详解 大纲 一.前言 二.安装与配置Tomcat 三.Tomcat 目录的结构 四.Tomcat 配置文件 注,本文的测试的操作系统为CentOS 6.4 ...

  5. SpringCloud及其组件详解

    SpringCloud及其组件详解 1.Spring Cloud 1.1 Spring Cloud和Dubbo的区别图解 1.2 微服务的技术栈 2.Spring Cloud 概述 2.1 Sprin ...

  6. Java中日志组件详解

    avalon-logkit Java中日志组件详解 lanhy 发布于 2020-9-1 11:35 224浏览 0收藏 作为开发人员,我相信您对日志记录工具并不陌生. Java还具有功能强大且功能强 ...

  7. Android中Intent组件详解

    Intent是不同组件之间相互通讯的纽带,封装了不同组件之间通讯的条件.Intent本身是定义为一个类别(Class),一个Intent对象表达一个目的(Goal)或期望(Expectation),叙 ...

  8. Android笔记——四大组件详解与总结

     android四大组件分别为activity.service.content provider.broadcast receiver. ------------------------------- ...

  9. Qt的Graphics-View框架和OpenGL结合详解

    Qt的Graphics-View框架和OpenGL结合详解 演示程序下载地址:这里 程序源代码下载地址:这里 这是一篇纯技术文,介绍了这一个月来我抽时间研究的成果. Qt中有一个非常炫的例子:Boxe ...

随机推荐

  1. Java中静态变量和实例变量的区别

    静态变量属于类的级别,而实例变量属于对象的级别. 主要区别有两点: 1,存放位置不同 类变量随着类的加载存在于方法区中,实例变量随着对象的对象的建立存在于堆内存中. 2,生命周期不同 类变量的生命周期 ...

  2. js中的数组去掉空值

    //result 是有空值的数组//r是处理好的数组var r = result.filter(function (s) { return s && s.trim();});

  3. mysql基础知识和pymysql

    一.视图 视图是指计算机数据库中的视图,是一个虚拟表,其内容由查询定义.同真实的表一样,视图包含一系列带有名称的列和行数据.但是,视图并不在数据库中以存储的数据值集形式存在.行和列数据来自由定义视图的 ...

  4. 可持久化+Trie || BZOJ 3261最大异或和 || Luogu P4735 最大异或和

    题面:最大异或和 代码: #include<cstdio> #include<cstring> #include<iostream> using namespace ...

  5. VMmare下安装redhat

    一.虚拟机必须安装在自定义的文件夹下,虚拟硬盘文件必须存放在自定义路径下(避免中文) 二.安装时选择linux类型时必须选择red hat enterprise linux 5 64位 三.操作系统名 ...

  6. CF839E Mother of Dragons 最大团 Bron-Kerbosch算法

    题意简述 给你一个\(n\)个节点的无向图\(G=\{V,E\}\)的邻接矩阵\(g\)和每个点的点权为\(s_i\),且\(\sum_{i=1}^n s_i = K\),要你求出\(\mathrm{ ...

  7. C++ GUI Qt4学习笔记03

    C++ GUI Qt4学习笔记03   qtc++spreadsheet文档工具resources 本章介绍创建Spreadsheet应用程序的主窗口 1.子类化QMainWindow 通过子类化QM ...

  8. Java课程作业02

    01. 一.设计思想: 第一种使用n!的公式直接计算,利用递归方法求n! 第二种使用递推的公式,利用递归返回求和. 二.程序流程图 三.源代码 import java.util.*;import ja ...

  9. 多线程之同时更改数据问题--启用lock

    using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.T ...

  10. Java——super

    在Java类中使用super来引用基类的成分.   [代码]