Flask第三方组件之flask_session
flask默认提供了session, 但是存在以下问题:
① session数据存在客户端, 不安全
② 大小有限制
③ 增加了客户端的压力
所以才产生了很多第三方的session机制, 我使用的是flask_session, 也是官方认可的一个.
安装flask_session
如何使用flask_session
导入flask_session
from flask_session import Session
重新封装flask的session机制
实例化一个Session对象, 将flask实例传进去
app = Flask(__name__) app.config["SESSION_TYPE"] = 'redis' # 如何存放session app.config["SESSION_REDIS"] = redis.Redis(host="192.168.233.128", port=6379) # 连接redis的配置 app.config["PERMANENT_SESSION_LIFETIME"] = timedelta(minutes=1) #session的有效期 Session(app)
flask默认的session是存放在浏览器中的, 这个使用flask_session, 我准备将session存放在redis中, 所以需要做这些配置
下面看看Session实例化时都做了些什么
Session对app做了些什么?
class Session(object): def __init__(self, app=None): self.app = app if app is not None: self.init_app(app)
形参app就是传进来的app实例, 所有就会执行self.init_app(app), 看看有做了什么?
def init_app(self, app): app.session_interface = self._get_interface(app)
这个函数很无聊, 就调用了一个self._get_interface(app), 其实session_interface就是flask所需要的一个操作session的接口, 在这里重新提供了一个接口, 当在进行session存取时候, 就会使用这个新的存取方式
def _get_interface(self, app): config = app.config.copy() # 将flask实例的配置config拷贝了一份 # 设置这些值, 如果已经存在就不会再设置了 config.setdefault('SESSION_TYPE', 'null') # session存储的类型 config.setdefault('SESSION_PERMANENT', True) config.setdefault('SESSION_USE_SIGNER', False) config.setdefault('SESSION_KEY_PREFIX', 'session:') # 存在数据中的key中前缀, 最后就会看到, 类似于session: uuid4 config.setdefault('SESSION_REDIS', None) # redis连接 config.setdefault('SESSION_MEMCACHED', None) config.setdefault('SESSION_FILE_DIR', os.path.join(os.getcwd(), 'flask_session')) config.setdefault('SESSION_FILE_THRESHOLD', 500) config.setdefault('SESSION_FILE_MODE', 384) config.setdefault('SESSION_MONGODB', None) config.setdefault('SESSION_MONGODB_DB', 'flask_session') config.setdefault('SESSION_MONGODB_COLLECT', 'sessions') config.setdefault('SESSION_SQLALCHEMY', None) config.setdefault('SESSION_SQLALCHEMY_TABLE', 'sessions') # 下面都是根据不从的session存储类型, 去获取不同的连接对象, 这个对象就可以存取session if config['SESSION_TYPE'] == 'redis': session_interface = RedisSessionInterface( config['SESSION_REDIS'], config['SESSION_KEY_PREFIX'], config['SESSION_USE_SIGNER'], config['SESSION_PERMANENT']) elif config['SESSION_TYPE'] == 'memcached': session_interface = MemcachedSessionInterface( config['SESSION_MEMCACHED'], config['SESSION_KEY_PREFIX'], config['SESSION_USE_SIGNER'], config['SESSION_PERMANENT']) elif config['SESSION_TYPE'] == 'filesystem': session_interface = FileSystemSessionInterface( config['SESSION_FILE_DIR'], config['SESSION_FILE_THRESHOLD'], config['SESSION_FILE_MODE'], config['SESSION_KEY_PREFIX'], config['SESSION_USE_SIGNER'], config['SESSION_PERMANENT']) elif config['SESSION_TYPE'] == 'mongodb': session_interface = MongoDBSessionInterface( config['SESSION_MONGODB'], config['SESSION_MONGODB_DB'], config['SESSION_MONGODB_COLLECT'], config['SESSION_KEY_PREFIX'], config['SESSION_USE_SIGNER'], config['SESSION_PERMANENT']) elif config['SESSION_TYPE'] == 'sqlalchemy': session_interface = SqlAlchemySessionInterface( app, config['SESSION_SQLALCHEMY'], config['SESSION_SQLALCHEMY_TABLE'], config['SESSION_KEY_PREFIX'], config['SESSION_USE_SIGNER'], config['SESSION_PERMANENT']) else: session_interface = NullSessionInterface() return session_interface # 返回可能操作数据库中的session的对象
看看能够操作session的对象长什么样子.
def __init__(self, redis, key_prefix, use_signer=False, permanent=True): if redis is None: from redis import Redis redis = Redis() self.redis = redis # 我配置的redis连接对象, 他就靠这个去连接数据库的 self.key_prefix = key_prefix # 定义的session key的前缀 self.use_signer = use_signer self.permanent = permanent
上面说了, flask操作session的session_interface变成了新的, 这个session_interface中肯定有存取session的方法, 到底是怎么存取的, follow me.
存放session, key就是 前缀:uuid4
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) # 1. 先进行删除 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, # 2. 在进行设置 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)
获取session, 根据用于请求时携带的session, 获取Redis种的数据
def open_session(self, app, request): sid = request.cookies.get(app.session_cookie_name) # 1.从cookie中获取存放在浏览器的session 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) # 2. 从redis中获取 前缀 + session 的值, 这个值就是你使用session["user"] = user, 存的值 if val is not None: # 3. 判断获取结果 try: data = self.serializer.loads(val) # 4. 取获取到的结果进行反序列化, 得到session中的数据 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)
到此为止就是flask_session的工作流程
flask_session的应用
下面是我做的一个完成的应用
from flask import Flask, session, request, render_template, redirect from flask import views from flask_session import Session import redis from datetime import timedelta # 当时使用了Session, 小写的flask原生的session依然需要导入, 因为设置session还是通过这个, 只不过存储session的方式改变了 app = Flask(__name__) app.config["SESSION_TYPE"] = 'redis' # 指定session存储的类型 app.config["SESSION_REDIS"] = redis.Redis(host="192.168.233.128", port=6379) # 创建redis连接 app.config["PERMANENT_SESSION_LIFETIME"] = timedelta(minutes=1) # 执行session的有效时间 Session(app) # 使用Session对session的存储机制重新定义 @app.before_request def confirm(): """ 校验用户是否已经登录 :return: """ if session.get("user") or request.path == "/login": return None else: return redirect('/login') class LoginView(views.MethodView): def get(self): return render_template("login.html") def post(self): u = request.form.get("username") p = request.form.get("password") if p == "wang": # 当用户登陆成功后, 在session中设置一个标识, 标识用户已经登录了 session["user"] = u return "登录成功" else: return "error" @app.route('/index', endpoint="index") def index(): return "index" app.add_url_rule("/login", view_func=LoginView.as_view("login")) if __name__ == '__main__': app.run(host="192.168.12.29", port=80, debug=True)