Open15

flaskのセッションメモ

merutinmerutin

最低限のコードを書く

app.py
from flask import Flask

app = Flask(__name__)

@app.route("/")
def hello_world():
    return "<p>Hello, World!</p>"
merutinmerutin
flask run

で起動して、http://127.0.0.1:5000 にアクセスするとCookieがセットされている。
NameはsessionでValueが eyJpZCI6InRlc3QifQ.YrVAGg.l9yOuZmHGsWHNsxbIQHiHuqMXQs

merutinmerutin

一応バージョンの確認をしておく

flask --version
Python 3.9.5
Flask 2.1.2
Werkzeug 2.1.2
merutinmerutin

flaskのsessions.pyでset_cookieしているあたりにログを仕込んで何が出ているのか調べてみる
https://github.com/pallets/flask/blob/main/src/flask/sessions.py#L412-L422

session.py
        val = self.get_signing_serializer(app).dumps(dict(session))  # type: ignore
+      print(dict(session))
+      print(val)
        response.set_cookie(
            name,
            val,  # type: ignore
            expires=expires,
            httponly=httponly,
            domain=domain,
            path=path,
            secure=secure,
            samesite=samesite,
        )

{'id': 'test'}eyJpZCI6InRlc3QifQ.YrVC5g.4-D6YpL2jzODCAEJd3toTfvgTp8 が出力できた

merutinmerutin

get_signing_serializerで返したクラスがsessionの値を変更しているっぽいので、中身を見てみる
関数はURLSafeTimedSerializerらしいがコード補完的には
itsdangerousというライブラリのserializer.pyのdumpの部分

https://github.com/pallets/itsdangerous/blob/main/src/itsdangerous/serializer.py#L202-L208

serializer.py
    def dumps(self, obj: _t.Any, salt: _t_opt_str_bytes = None) -> _t_str_bytes:
        """Returns a signed string serialized with the internal
        serializer. The return value can be either a byte or unicode
        string depending on the format of the internal serializer.
        """
        payload = want_bytes(self.dump_payload(obj))
        rv = self.make_signer(salt).sign(payload)

+        print(payload)
+        print(rv)
+       print(salt)
        if self.is_text_serializer:
            return rv.decode("utf-8")

        return rv

雑にログを仕込んでみると以下のような結果になる

b'eyJpZCI6InRlc3QifQ'
b'eyJpZCI6InRlc3QifQ.YrVHSw._CNZ3421DGZWaHoltGppQ2y1H84'
None
merutinmerutin

flask_loginを入れてみる

 pip install flask_login

flask-login-0.6.1が入った。

簡単に実装してみる

app.py
from flask import Flask, session
import flask_login

app = Flask(__name__)

app.secret_key = 'sample'
login_manager = flask_login.LoginManager()
login_manager.init_app(app)

@app.route("/")
def hello_world():
	session['id'] = 'test'
	return "<p>Hello, World!</p>"

class User(flask_login.UserMixin):
    def __init__(self, user_id):
        self.id = user_id

@login_manager.user_loader
def load_user(user_id):
    return User(user_id)

@app.route('/login', methods=['GET', 'POST'])
def login():
    # validationチェック
		user = User('sample')
		flask_login.login_user(user)
		# ログインに成功したらmemberページへ飛びます
		return "OK"

merutinmerutin

loginにアクセスすると、入っているcookieの値が変わっている
.eJwlzk0KAjEMQOG7dO2izU_bzGWGpE1QUJAZXYl3t-D-e_A-aY_Dz2vaXsfbL2m_zbQlxelDQ-boAoA0sxTpql4kR2m1VvUscwYZhQSbmiPDdK6Nci4yGBoKZM_sVQ07mHVUUFvBMo24cQmvdegiyoSNZYB1KkaW1sj79ON_c-rjeff0_QHrSTG5.YrV1hA.Eh9FtmBTEtgnYo-ZO39vrQwUQGY

merutinmerutin

どうやら、一定上の文字列の場合はzipで圧縮しているらしい。

merutinmerutin

試しにIDに長い文字列をセットしたら同じような挙動になった

@app.route("/")
def hello_world():
	session['id'] = 'test'
	session['_id'] = '0123456789123456789012345678901234567890123456789012345678901234567890'
	return "<p>Hello, World!</p>"

.eJyrVorPTFGyUjIwNDI2MTUzt7CEMwxIZynpKIFNK0ktLlGqBQCreBS9.YrV8Tg.ZVfi8-rmWeJkzqdgGbbWU134iNk

merutinmerutin

flask_loginを利用している場合はremember_toknの値がcookieに入っている。
flask_loginではセッションのチェック時にremember_tokenの値をまず利用して、取得できなかった場合にsessionの値を利用している