02-Flask session

Session 简介

默认情况下 Flask 的 session 保存在客户端的 cookie 中。它是在 Cookies 的基础上实现的,并且对 Cookies 进行密钥签名要使用会话,你需要设置一个密钥:app.secret_key

给 session 设置密钥一般有三种方法:

  • 手动指定 session 密钥:app.secret = 'A0Zr98j/3yX R~XHH!jmN\]LWX/,?RT'
    • 该方法风险性高,优点是便于调试
  • 使用 app.secret = secrets.token_hex(16) 生成随机的十六进制字符串作为密钥
  • 使用 app.secret = os.urandom(24) 生成随机的二进制字符串作为密钥
    • 推荐使用第二种和第三种,并且定期更新密钥,防止数据泄露

session 使用。操作 session 就像操作 Python 中的字典一样:

  • 设置:session['username'] = 'xxx'
  • 删除:session.pop('username', None)
  • 获取:
    • value = session["key"] 如果“key”不存在,将会抛出异常
    • value = session.get("key") 如果“key”不存在,将返回 None(推荐使用)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
from flask import Flask, session, redirect, url_for, escape, request

app = Flask(__name__)
# set the secret key. keep this really secret:
app.secret_key = 'A0Zr98j/3yX R~XHH!jmN]LWX/,?RT'
# app.config['SECRET_KEY'] = os.urandom(24) #设置密钥
# app.config['PERMANENT_SESSION_LIFETIME'] = timedelta(days=7) # 配置7天有效


# 设置session
@app.route('/')
def set():
session['username'] = 'zhangvalue' # 设置“字典”键值对
session.permanent = True # 设置session的有效时间,长期有效,一个月的时间有效,
# 具体看上面的配置时间具体的,没有上面设置的时间就是一个月有效
print(app.config['SECRET_KEY'])
return 'success'


# 读取session
@app.route('/get')
def get():
# 第一种session获取如果不存在会报错
# session['username']
# 推荐使用session.get('username')
# session.get('username')
return session.get('username')


# 删除session
@app.route('/delete/')
def delete():
print(session.get('username'), session.pop('username', None))
# 或者 session['username'] = False
print(session.get('username'))
return 'success'


# 清除session中所有数据
@app.route('/clear')
def clear():
print(session.get('username'))
# 清除session中所有数据
session.clear
print(session.get('username'))
return 'success'


if __name__ == '__main__':
app.run(host='0.0.0.0', port='5010')

session 的配置

1
2
3
4
5
6
7
'SESSION_COOKIE_NAME': 'session',       # 设置 cookie 中 session 的名字
'SESSION_COOKIE_DOMAIN': None,
'SESSION_COOKIE_PATH': None,
'SESSION_COOKIE_HTTPONLY': True,
'SESSION_COOKIE_SECURE': False,
'SESSION_REFRESH_EACH_REQUEST': True, # 是否每次都跟新
'PERMANENT_SESSION_LIFETIME': timedelta(days=31) # 设置session的过期时间

session 原理

  • session 序列化机制。
    当我们开启 session 时,设置 session["username"]="stzz",Flask 会帮我们创建一个字典 {"username":"stzz"},然后通过 secret_key + 时间戳 + 签名 经过加密生成一个字符串。

  • session 反序列化机制。
    当客户端发送请求时,request 请求会带上 cookie,也就是 session 中的数据存储在其中,这个数据就是之前加密后的字符串,发送到后端后,Flask 会通过 secret_key 去解密 session 中的加密字符串,从而获取 {"username":"stzz"} 从而来验证是否登录。

自定义 session

由于 Flask 框架自身的问题,cookie 在客户端是可读的,这也就造成 session 可能会被破解,因此我们需要进一步提升 session 安全

可以通过重写 open-session 和 save-session 方法,让 session 存储在内存中或者 Redis 中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
############################# 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) # 在cookie中通过配置的name获取session
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)

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

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):
"""程序结束前执行,可以保存session中所有的值;如: 保存到resit,写入到用户cookie"""
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))

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

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)

使用第三方库 flask-Session

如果不想自己写,可以使用 Flask-Session 扩展,将会话数据存储在服务器端。Flask-Session 支持多种存储后端,如文件系统、Redis、Memcached 等。

安装:

1
2
pip3 install redis
pip3 install flask-session

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
from redis import Redis
from flask import Flask, session, redirect
from flask.ext.session import Session


app = Flask(__name__)
app.debug = True
app.secret_key = 'asdfasdfasd'

# 配置 Flask-Session
app.config['SESSION_TYPE'] = 'redis'
app.config['SESSION_REDIS'] = Redis(host='192.168.0.94',port='6379')
Session(app) # 初始化 Flask-Session


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


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


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

02-Flask session
https://flepeng.github.io/021-Python-32-框架-Flask-02-Flask-session/
作者
Lepeng
发布于
2024年9月25日
许可协议