flask—flask_session

flask 默认提供的 session 功能还是很简单的,满足了基本的功能。但是我们看到 flask 把 session 的数据都保存在客户端的 cookie 中,这里只有用户名还好,如果有一些私密的数据(比如密码,账户余额等等),就会造成严重的安全问题。所以我们会使用 flask-session 这个三方的库,它把数据保存在服务器端(本地文件、redis、memcached),客户端只拿到一个 sessionid。 

安装flask_session

pip install flask_session

使用:

#!/usr/bin/env python
# -*- coding: utf-8 -*-
__author__ = 'Wu'

from redis import Redis
from flask import Flask, session
from flask_session import RedisSessionInterface

app = Flask(__name__)
app.secret_key = 'request-se1@3'

conn = Redis(......)
app.session_interface = RedisSessionInterface(conn, key_prefix='se__', use_signer=True, permanent=True)
# key_prefix:设置redis中key的前缀
# use_signer:是否使用签名算法
# permanent:设置是否持久化,如果为False,那么设置的cookie超时时间将无效。cookie将会在关闭浏览器时失效。


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


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

  

源码流程:

app.session_interface = RedisSessionInterface(conn, key_prefix='se')

通过RedisSessionInterface实例化对象app.session_interface,重新定义了app.session_interface。下面我们来看一下RedisSessionInterface类的__init__方法。

    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   # 设置redis中key的前缀
        self.use_signer = use_signer
        self.permanent = permanent

下面是flask中的open_session方法。

    def open_session(self, request):    
        return self.session_interface.open_session(self, request)

由于我们重新定义了app.session_interface,所以open_session方法会执行RedisSessionInterface类中的open_session方法。

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_prefixa
        # 设置redis中key的前缀
        self.use_signer = use_signer
        # 是否使用签名算法
        self.permanent = permanent
        # 设置是否持久化,如果为False,那么设置的cookie超时时间将无效。cookie将会在关闭浏览器时失效。

以上是RedisSessionInterface类的构造方法,在构造方法中设置了一些初始值。

    def open_session(self, app, request):
        # 去cookie中获取key为session对应的值
        sid = request.cookies.get(app.session_cookie_name)
        if not sid:
            # 创建一个随机字符串
            # sid = 6dc1f9bb-1bdd-4bdf-8e3b-b8b98495466e
            sid = self._generate_sid()

            # 生成一个特殊的字典(session)并返回  {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')
            # 根据前缀和随机字符串,去redis中获取值
            # sid = se__6dc1f9bb-1bdd-4bdf-8e3b-b8b98495466e
        val = self.redis.get(self.key_prefix + sid)
        # 如果能从redis中获取到值。
        if val is not None:
            try:
                # 将redis中存放的值loads成一个字典
                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)

上面就是RedisSessionInterface类中的open_session方法。open_session方法处理完成后由save_session方法,将session序列化后保存到redis中,并设置到cookie中。

    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)
        # 将session中保存的特殊字典,转换成普通字典,再序列化成字符串
        val = self.serializer.dumps(dict(session))

        # 将序列化后的字符串保存到redis中
        """
            {
                se__6dc1f9bb-1bdd-4bdf-8e3b-b8b98495466e:{'username':test, sid:6dc1f9bb-1bdd-4bdf-8e3b-b8b98495466e}
            }
        """
        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
        # session:session_id , session_id就是利用uuid生成的随机字符串
        # expires用于设置超时时间。
        response.set_cookie(app.session_cookie_name, session_id,
                            expires=expires, httponly=httponly,
                            domain=domain, path=path, secure=secure)

 

posted @ 2018-03-29 22:50  流星之泪  阅读(349)  评论(0)    收藏  举报