WEB, Flask

参考:

https://blog.csdn.net/nunchakushuang/article/details/74652877
http://portal.xiaoxiangzi.com/Programme/Python/7890.html
https://www.zhihu.com/question/19786827
http://www.woshipm.com/pd/864133.html

  1. Session是在服务端保存的一个数据结构,用来跟踪用户的状态,这个数据可以保存在集群、数据库、文件中;
  2. Cookie是客户端保存用户信息的一种机制,用来记录用户的一些信息,也是实现Session的一种方式。
  3. cookie在进行秘钥签名时要使用会话,所以要设置secret key.
  4. 设置:session['username'] = 'xxx'
  5. 删除:session.pop('username', None)
  6. session的使用方式是客户端cookie里存id,服务端session存用户数据,客户端访问服务端的时候,根据id找用户数据。

6. 请求到来时,无论什么“数据库”的特有的session,从Cookie取出后,都是存储在Flask的Local里的ctx中的。(有关Local可以看上下文管理的第6点)从app.call__方法查看ctx.push()源码。 因为里面有session_interface,默认的是SecureCookieSessionInterface,可以自定义,例如改成app.session_interface = RedisSessionInterface(conn,key_prefix='',use_signer=False)。

7. 请求结束时,第三方组件的session存在“数据库”中。默认的SecureCookieSessionInterface中sessionID和session的值加密后都放在Cookie中。(默认的是client-side session)

8. Cookie有长度限制和加密后也不安全,所以session最好放在“数据库”中。(Redis是server-side session)

1. 基本使用

from flask import Flask, session, redirect, url_for, escape, request

app = Flask(__name__)


@app.route('/')
def index():
    if 'username' in session:
        return 'Logged in as %s' % escape(session['username'])
    return 'You are not logged in'


@app.route('/login', methods=['GET', 'POST'])
def login():
    if request.method == 'POST':
        session['username'] = request.form['username']
        return redirect(url_for('index'))
    return '''
        <form action="" method="post">
            <p><input type=text name=username>
            <p><input type=submit value=Login>
        </form>
    '''


@app.route('/logout')
def logout():
    # remove the username from the session if it's there
    session.pop('username', None)
    return redirect(url_for('index'))


# set the secret key.  keep this really secret:
app.secret_key = 'A0Zr98j/3yX R~XHH!jmN]LWX/,?RT'

2. 自定义session

pip3
install
Flask - Session

run.py
from flask import Flask
from flask import session
from pro_flask.utils.session import MySessionInterface

app = Flask(__name__)

app.secret_key = 'A0Zr98j/3yX R~XHH!jmN]LWX/,?RT'
app.session_interface = MySessionInterface()


@app.route('/login.html', methods=['GET', "POST"])
def login():
    print(session)
    session['user1'] = 'alex'
    session['user2'] = 'alex'
    del session['user2']

    return "内容"


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

session.py
# !/usr/bin/env python
# -*- coding:utf-8 -*-
import uuid
import json
from flask.sessions import SessionInterface
from flask.sessions import SessionMixin
from itsdangerous import Signer, BadSignature, want_bytes


class MySession(dict, SessionMixin):
    def __init__(self, initial=None, sid=None):
        self.sid = sid
        self.initial = initial
        super(MySession, self).__init__(initial or ())

    def __setitem__(self, key, value):
        super(MySession, self).__setitem__(key, value)

    def __getitem__(self, item):
        return super(MySession, self).__getitem__(item)

    def __delitem__(self, key):
        super(MySession, self).__delitem__(key)


class MySessionInterface(SessionInterface):
    session_class = MySession
    container = {}

    def __init__(self):
        import redis
        self.redis = redis.Redis()

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

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

    def open_session(self, app, request):
        """
        程序刚启动时执行,需要返回一个session对象
        """
        sid = request.cookies.get(app.session_cookie_name)
        if not sid:
            sid = self._generate_sid()
            return self.session_class(sid=sid)

        signer = self._get_signer(app)
        try:
            sid_as_bytes = signer.unsign(sid)
            sid = sid_as_bytes.decode()
        except BadSignature:
            sid = self._generate_sid()
            return self.session_class(sid=sid)

        # session保存在redis中
        # val = self.redis.get(sid)
        # session保存在内存中
        val = self.container.get(sid)

        if val is not None:
            try:
                data = json.loads(val)
                return self.session_class(data, sid=sid)
            except:
                return self.session_class(sid=sid)
        return self.session_class(sid=sid)

    def save_session(self, app, session, response):
        domain = self.get_cookie_domain(app)
        path = self.get_cookie_path(app)
        httponly = self.get_cookie_httponly(app)
        secure = self.get_cookie_secure(app)
        expires = self.get_expiration_time(app, session)

        val = json.dumps(dict(session))

        # session保存在redis中
        # self.redis.setex(name=session.sid, value=val, time=app.permanent_session_lifetime)
        # session保存在内存中
        self.container.setdefault(session.sid, val)

        session_id = self._get_signer(app).sign(want_bytes(session.sid))

        response.set_cookie(app.session_cookie_name, session_id,
                            expires=expires, httponly=httponly,
                            domain=domain, path=path, secure=secure)

3. Flask中的session处理机制(核心)

内置session:将session和session对应的数据保存在加密cookie中实现

- 请求刚到来:获取SessionID(随机字符串),存在则去“数据库”(cookie(cookie就不是数据库),memcache, redis, 内存,数据库,集群,文件...)中获取原来的个人数据,否则创建一个空容器。 --> 内存:对象(SessionID:{放置数据的容器})
	# 1. obj = 创建SecureCookieSessionInterface()
	# 2. obj = open_session(self.request) = SecureCookieSession()
	# self.session = SecureCookieSession()对象。 
	self.session = self.app.open_session(self.request)


- 处理请求,视图:操作内存中 对象(SessionID,{放置数据的容器})
- 请求终止,响应:内存中 对象(SessionID,{放置数据的容器})
		- 将session保存到“数据库”
		- 把SessionID写到用户cookie中。		

自定义session

请求刚到来:
	# 创建特殊字典,并添加到Local的ctx中。从app.__call__方法查看ctx.push()源码
	# 调用关系:
	#	self.session_interface.open_session(self, request)
	# 	由于默认app中的session_interface=SecureCookieSessionInterface()
	#		SecureCookieSessionInterface().open_session(self, request)
	# 	自定义app中的session_interface=MySessionInterFace()
	#		MySessionInterFace().open_session(self, request)
	self.session = self.app.open_session(self.request)

处理请求,调用:
	session =  LocalProxy -> 偏函数 -> LocalStack -> Local -> ctx --> session

请求终止,响应:
	# 	由于默认app中的session_interface=SecureCookieSessionInterface()
	#		SecureCookieSessionInterface().save_session(self, app, session, response)
	# 	自定义app中的session_interface=MySessionInterFace()
	#		MySessionInterFace().save_session(self, app, session, response)

第三方session,flask_session组件,from flask_session import sessions
请求结束时,把该session存储在Redis中,并将sessionID写进Cookie中。(看def save_session源码)

from flask import Flask,session
from flask_session import RedisSessionInterface

app = Flask(__name__)
app.secret_key = 'xxxxx'

from redis import Redis
conn = Redis()
app.session_interface = RedisSessionInterface(conn,key_prefix='__',use_signer=False, permanent=False)


@app.route('/')
def index():
	session['xxx'] = 123
	return 'Index'


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

RedisSessionInterface里与SecureCookieSessionInterface(默认的)一样有open_session,save_session方法。方法里return一个特有的session(RedisSession,看源码),请求到来时,session存在Local的ctx里。

flask_session还有其他“数据库”,存储对应特有的session。特有的session对应的数据存储在这些“数据库”中。

4. 问题:设置cookie时,如何设定关闭浏览器则cookie失效?

例如在SecureCookieSessionInterface中的save_session里设置response.set_cookie('k','v',exipre=None)
或者app.session_interface = RedisSessionInterface(conn,key_prefix='__',use_signer=False, permanent=False)
原理都是exipre=None

原文地址:https://www.cnblogs.com/allen2333/p/9016003.html