亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

flask庫中sessions.py的使用小結(jié)

 更新時間:2025年07月27日 11:26:48   作者:倔強(qiáng)老呂  
在Flask中Session是一種用于在不同請求之間存儲用戶數(shù)據(jù)的機(jī)制,Session默認(rèn)是基于客戶端Cookie的,但數(shù)據(jù)會經(jīng)過加密簽名,防止篡改,下面就來具體介紹一下如何使用

在 Flask 中,Session(會話) 是一種用于在不同請求之間存儲用戶數(shù)據(jù)的機(jī)制。Flask 的 Session 默認(rèn)是基于 客戶端 Cookie 的,但數(shù)據(jù)會經(jīng)過加密簽名,防止篡改(但內(nèi)容本身是可讀的,除非額外加密)。

1. Flask Session 的基本使用

(1) 啟用 Session

Flask 的 Session 基于 flask.session,它是一個類似字典的對象。使用時需要設(shè)置 SECRET_KEY(用于簽名 Session 數(shù)據(jù),防止篡改):

from flask import Flask, session

app = Flask(__name__)
app.secret_key = 'your-secret-key-here'  # 必須設(shè)置,否則 Session 無法工作

(2) 存儲和讀取 Session

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

app = Flask(__name__)
app.secret_key = 'your-secret-key'

@app.route('/login')
def login():
    session['username'] = 'admin'  # 存儲 Session
    session['logged_in'] = True    # Session 可以存儲任意可序列化的數(shù)據(jù)
    return "Logged in!"

@app.route('/dashboard')
def dashboard():
    if session.get('logged_in'):
        return f"Welcome, {session['username']}!"
    else:
        return redirect(url_for('login'))

@app.route('/logout')
def logout():
    session.pop('username', None)  # 移除 Session 中的某個鍵
    session.clear()                # 清空整個 Session
    return "Logged out!"

(3) Session 的持久性(Permanent Session)

默認(rèn)情況下,Session 在瀏覽器關(guān)閉后失效。如果要讓 Session 長期有效(基于 PERMANENT_SESSION_LIFETIME 配置):

from datetime import timedelta

app.config['PERMANENT_SESSION_LIFETIME'] = timedelta(days=7)  # 7天后過期

@app.route('/login')
def login():
    session.permanent = True  # 啟用持久 Session
    session['username'] = 'admin'
    return "Logged in (persistent)!"

2. Flask Session 的工作原理

(1) 默認(rèn)實現(xiàn)(SecureCookieSession)

Flask 的 Session 默認(rèn)使用 SecureCookieSession,數(shù)據(jù)存儲在 客戶端 Cookie 中,但經(jīng)過簽名(防止篡改)。

數(shù)據(jù)格式

{
    "username": "admin",
    "_fresh": True,
    "_id": "abc123...",
    "_permanent": True
}

簽名機(jī)制:使用 itsdangerous 庫進(jìn)行簽名,確保數(shù)據(jù)不被篡改(但內(nèi)容可讀)。

(2) 如何修改 Session 存儲方式?

默認(rèn)的 Cookie Session 有大小限制(通常 4KB),如果需要存儲大量數(shù)據(jù),可以改用 服務(wù)端 Session(如 Redis、數(shù)據(jù)庫):

from flask_session import Session  # 需要安裝 flask-session

app.config['SESSION_TYPE'] = 'redis'  # 可選 redis, memcached, filesystem 等
app.config['SESSION_PERMANENT'] = True
app.config['SESSION_USE_SIGNER'] = True  # 是否簽名 Cookie
app.config['SESSION_KEY_PREFIX'] = 'myapp:'  # Redis 鍵前綴

Session(app)  # 替換默認(rèn)的 Session 實現(xiàn)

然后 flask.session 會自動使用 Redis 存儲數(shù)據(jù),而不是 Cookie。

3. Session 的安全配置

(1) 安全相關(guān)的 Cookie 設(shè)置

app.config.update(
    SESSION_COOKIE_NAME='my_session',       # Cookie 名稱
    SESSION_COOKIE_HTTPONLY=True,          # 禁止 JavaScript 訪問
    SESSION_COOKIE_SECURE=True,            # 僅 HTTPS 傳輸
    SESSION_COOKIE_SAMESITE='Lax',         # 防 CSRF(Strict/Lax/None)
    SESSION_COOKIE_PARTITIONED=True,       # 跨站 Cookie 隔離(CHIPS)
)

(2) 防止 Session 劫持

  • 使用 SESSION_USE_SIGNER=True(默認(rèn)啟用)防止篡改。
  • 避免在 Session 中存儲敏感信息(如密碼),因為 Cookie 內(nèi)容可被用戶讀?。词购灻?/li>

4. 常見問題

(1)RuntimeError: The session is unavailable because no secret key was set.

原因:未設(shè)置 SECRET_KEY。
解決

app.secret_key = 'your-secret-key' # 必須設(shè)置

(2) Session 數(shù)據(jù)不持久

原因:默認(rèn) Session 在瀏覽器關(guān)閉后失效。
解決

session.permanent = True
app.config['PERMANENT_SESSION_LIFETIME'] = timedelta(days=30)

(3) 如何強(qiáng)制 Session 過期?

@app.route('/clear_session')
def clear_session():
    session.clear()  # 清空 Session
    return "Session cleared!"

5. 總結(jié)

特性說明
默認(rèn)存儲客戶端 Cookie(簽名防篡改)
持久性session.permanent = True + PERMANENT_SESSION_LIFETIME
安全配置HTTPOnly、Secure、SameSite
擴(kuò)展存儲使用 flask-session 支持 Redis、數(shù)據(jù)庫等
適用場景小型數(shù)據(jù)(如用戶登錄狀態(tài)),大數(shù)據(jù)需用服務(wù)端存儲

如果需要更安全的 Session 方案,建議:

  • 使用 服務(wù)端 Session(Redis) 替代 Cookie。
  • 避免存儲敏感數(shù)據(jù)在 Session 中。
  • 啟用 Secure + SameSite 防 CSRF。

Sessions.py

from __future__ import annotations

import collections.abc as c
import hashlib
import typing as t
from collections.abc import MutableMapping
from datetime import datetime
from datetime import timezone

from itsdangerous import BadSignature
from itsdangerous import URLSafeTimedSerializer
from werkzeug.datastructures import CallbackDict

from .json.tag import TaggedJSONSerializer

if t.TYPE_CHECKING:  # pragma: no cover
    import typing_extensions as te

    from .app import Flask
    from .wrappers import Request
    from .wrappers import Response


class SessionMixin(MutableMapping[str, t.Any]):
    """Expands a basic dictionary with session attributes."""

    @property
    def permanent(self) -> bool:
        """This reflects the ``'_permanent'`` key in the dict."""
        return self.get("_permanent", False)

    @permanent.setter
    def permanent(self, value: bool) -> None:
        self["_permanent"] = bool(value)

    #: Some implementations can detect whether a session is newly
    #: created, but that is not guaranteed. Use with caution. The mixin
    # default is hard-coded ``False``.
    new = False

    #: Some implementations can detect changes to the session and set
    #: this when that happens. The mixin default is hard coded to
    #: ``True``.
    modified = True

    #: Some implementations can detect when session data is read or
    #: written and set this when that happens. The mixin default is hard
    #: coded to ``True``.
    accessed = True


class SecureCookieSession(CallbackDict[str, t.Any], SessionMixin):
    """Base class for sessions based on signed cookies.

    This session backend will set the :attr:`modified` and
    :attr:`accessed` attributes. It cannot reliably track whether a
    session is new (vs. empty), so :attr:`new` remains hard coded to
    ``False``.
    """

    #: When data is changed, this is set to ``True``. Only the session
    #: dictionary itself is tracked; if the session contains mutable
    #: data (for example a nested dict) then this must be set to
    #: ``True`` manually when modifying that data. The session cookie
    #: will only be written to the response if this is ``True``.
    modified = False

    #: When data is read or written, this is set to ``True``. Used by
    # :class:`.SecureCookieSessionInterface` to add a ``Vary: Cookie``
    #: header, which allows caching proxies to cache different pages for
    #: different users.
    accessed = False

    def __init__(
        self,
        initial: c.Mapping[str, t.Any] | c.Iterable[tuple[str, t.Any]] | None = None,
    ) -> None:
        def on_update(self: te.Self) -> None:
            self.modified = True
            self.accessed = True

        super().__init__(initial, on_update)

    def __getitem__(self, key: str) -> t.Any:
        self.accessed = True
        return super().__getitem__(key)

    def get(self, key: str, default: t.Any = None) -> t.Any:
        self.accessed = True
        return super().get(key, default)

    def setdefault(self, key: str, default: t.Any = None) -> t.Any:
        self.accessed = True
        return super().setdefault(key, default)


class NullSession(SecureCookieSession):
    """Class used to generate nicer error messages if sessions are not
    available.  Will still allow read-only access to the empty session
    but fail on setting.
    """

    def _fail(self, *args: t.Any, **kwargs: t.Any) -> t.NoReturn:
        raise RuntimeError(
            "The session is unavailable because no secret "
            "key was set.  Set the secret_key on the "
            "application to something unique and secret."
        )

    __setitem__ = __delitem__ = clear = pop = popitem = update = setdefault = _fail  # noqa: B950
    del _fail


class SessionInterface:
    """The basic interface you have to implement in order to replace the
    default session interface which uses werkzeug's securecookie
    implementation.  The only methods you have to implement are
    :meth:`open_session` and :meth:`save_session`, the others have
    useful defaults which you don't need to change.

    The session object returned by the :meth:`open_session` method has to
    provide a dictionary like interface plus the properties and methods
    from the :class:`SessionMixin`.  We recommend just subclassing a dict
    and adding that mixin::

        class Session(dict, SessionMixin):
            pass

    If :meth:`open_session` returns ``None`` Flask will call into
    :meth:`make_null_session` to create a session that acts as replacement
    if the session support cannot work because some requirement is not
    fulfilled.  The default :class:`NullSession` class that is created
    will complain that the secret key was not set.

    To replace the session interface on an application all you have to do
    is to assign :attr:`flask.Flask.session_interface`::

        app = Flask(__name__)
        app.session_interface = MySessionInterface()

    Multiple requests with the same session may be sent and handled
    concurrently. When implementing a new session interface, consider
    whether reads or writes to the backing store must be synchronized.
    There is no guarantee on the order in which the session for each
    request is opened or saved, it will occur in the order that requests
    begin and end processing.

    .. versionadded:: 0.8
    """

    #: :meth:`make_null_session` will look here for the class that should
    #: be created when a null session is requested.  Likewise the
    #: :meth:`is_null_session` method will perform a typecheck against
    #: this type.
    null_session_class = NullSession

    #: A flag that indicates if the session interface is pickle based.
    #: This can be used by Flask extensions to make a decision in regards
    #: to how to deal with the session object.
    #:
    #: .. versionadded:: 0.10
    pickle_based = False

    def make_null_session(self, app: Flask) -> NullSession:
        """Creates a null session which acts as a replacement object if the
        real session support could not be loaded due to a configuration
        error.  This mainly aids the user experience because the job of the
        null session is to still support lookup without complaining but
        modifications are answered with a helpful error message of what
        failed.

        This creates an instance of :attr:`null_session_class` by default.
        """
        return self.null_session_class()

    def is_null_session(self, obj: object) -> bool:
        """Checks if a given object is a null session.  Null sessions are
        not asked to be saved.

        This checks if the object is an instance of :attr:`null_session_class`
        by default.
        """
        return isinstance(obj, self.null_session_class)

    def get_cookie_name(self, app: Flask) -> str:
        """The name of the session cookie. Uses``app.config["SESSION_COOKIE_NAME"]``."""
        return app.config["SESSION_COOKIE_NAME"]  # type: ignore[no-any-return]

    def get_cookie_domain(self, app: Flask) -> str | None:
        """The value of the ``Domain`` parameter on the session cookie. If not set,
        browsers will only send the cookie to the exact domain it was set from.
        Otherwise, they will send it to any subdomain of the given value as well.

        Uses the :data:`SESSION_COOKIE_DOMAIN` config.

        .. versionchanged:: 2.3
            Not set by default, does not fall back to ``SERVER_NAME``.
        """
        return app.config["SESSION_COOKIE_DOMAIN"]  # type: ignore[no-any-return]

    def get_cookie_path(self, app: Flask) -> str:
        """Returns the path for which the cookie should be valid.  The
        default implementation uses the value from the ``SESSION_COOKIE_PATH``
        config var if it's set, and falls back to ``APPLICATION_ROOT`` or
        uses ``/`` if it's ``None``.
        """
        return app.config["SESSION_COOKIE_PATH"] or app.config["APPLICATION_ROOT"]  # type: ignore[no-any-return]

    def get_cookie_httponly(self, app: Flask) -> bool:
        """Returns True if the session cookie should be httponly.  This
        currently just returns the value of the ``SESSION_COOKIE_HTTPONLY``
        config var.
        """
        return app.config["SESSION_COOKIE_HTTPONLY"]  # type: ignore[no-any-return]

    def get_cookie_secure(self, app: Flask) -> bool:
        """Returns True if the cookie should be secure.  This currently
        just returns the value of the ``SESSION_COOKIE_SECURE`` setting.
        """
        return app.config["SESSION_COOKIE_SECURE"]  # type: ignore[no-any-return]

    def get_cookie_samesite(self, app: Flask) -> str | None:
        """Return ``'Strict'`` or ``'Lax'`` if the cookie should use the
        ``SameSite`` attribute. This currently just returns the value of
        the :data:`SESSION_COOKIE_SAMESITE` setting.
        """
        return app.config["SESSION_COOKIE_SAMESITE"]  # type: ignore[no-any-return]

    def get_cookie_partitioned(self, app: Flask) -> bool:
        """Returns True if the cookie should be partitioned. By default, uses
        the value of :data:`SESSION_COOKIE_PARTITIONED`.

        .. versionadded:: 3.1
        """
        return app.config["SESSION_COOKIE_PARTITIONED"]  # type: ignore[no-any-return]

    def get_expiration_time(self, app: Flask, session: SessionMixin) -> datetime | None:
        """A helper method that returns an expiration date for the session
        or ``None`` if the session is linked to the browser session.  The
        default implementation returns now + the permanent session
        lifetime configured on the application.
        """
        if session.permanent:
            return datetime.now(timezone.utc) + app.permanent_session_lifetime
        return None

    def should_set_cookie(self, app: Flask, session: SessionMixin) -> bool:
        """Used by session backends to determine if a ``Set-Cookie`` header
        should be set for this session cookie for this response. If the session
        has been modified, the cookie is set. If the session is permanent and
        the ``SESSION_REFRESH_EACH_REQUEST`` config is true, the cookie is
        always set.

        This check is usually skipped if the session was deleted.

        .. versionadded:: 0.11
        """

        return session.modified or (
            session.permanent and app.config["SESSION_REFRESH_EACH_REQUEST"]
        )

    def open_session(self, app: Flask, request: Request) -> SessionMixin | None:
        """This is called at the beginning of each request, after
        pushing the request context, before matching the URL.

        This must return an object which implements a dictionary-like
        interface as well as the :class:`SessionMixin` interface.

        This will return ``None`` to indicate that loading failed in
        some way that is not immediately an error. The request
        context will fall back to using :meth:`make_null_session`
        in this case.
        """
        raise NotImplementedError()

    def save_session(
        self, app: Flask, session: SessionMixin, response: Response
    ) -> None:
        """This is called at the end of each request, after generating
        a response, before removing the request context. It is skipped
        if :meth:`is_null_session` returns ``True``.
        """
        raise NotImplementedError()


session_json_serializer = TaggedJSONSerializer()


def _lazy_sha1(string: bytes = b"") -> t.Any:
    """Don't access ``hashlib.sha1`` until runtime. FIPS builds may not include
    SHA-1, in which case the import and use as a default would fail before the
    developer can configure something else.
    """
    return hashlib.sha1(string)


class SecureCookieSessionInterface(SessionInterface):
    """The default session interface that stores sessions in signed cookies
    through the :mod:`itsdangerous` module.
    """

    #: the salt that should be applied on top of the secret key for the
    #: signing of cookie based sessions.
    salt = "cookie-session"
    #: the hash function to use for the signature.  The default is sha1
    digest_method = staticmethod(_lazy_sha1)
    #: the name of the itsdangerous supported key derivation.  The default
    #: is hmac.
    key_derivation = "hmac"
    #: A python serializer for the payload.  The default is a compact
    #: JSON derived serializer with support for some extra Python types
    #: such as datetime objects or tuples.
    serializer = session_json_serializer
    session_class = SecureCookieSession

    def get_signing_serializer(self, app: Flask) -> URLSafeTimedSerializer | None:
        if not app.secret_key:
            return None

        keys: list[str | bytes] = []

        if fallbacks := app.config["SECRET_KEY_FALLBACKS"]:
            keys.extend(fallbacks)

        keys.append(app.secret_key)  # itsdangerous expects current key at top
        return URLSafeTimedSerializer(
            keys,  # type: ignore[arg-type]
            salt=self.salt,
            serializer=self.serializer,
            signer_kwargs={
                "key_derivation": self.key_derivation,
                "digest_method": self.digest_method,
            },
        )

    def open_session(self, app: Flask, request: Request) -> SecureCookieSession | None:
        s = self.get_signing_serializer(app)
        if s is None:
            return None
        val = request.cookies.get(self.get_cookie_name(app))
        if not val:
            return self.session_class()
        max_age = int(app.permanent_session_lifetime.total_seconds())
        try:
            data = s.loads(val, max_age=max_age)
            return self.session_class(data)
        except BadSignature:
            return self.session_class()

    def save_session(
        self, app: Flask, session: SessionMixin, response: Response
    ) -> None:
        name = self.get_cookie_name(app)
        domain = self.get_cookie_domain(app)
        path = self.get_cookie_path(app)
        secure = self.get_cookie_secure(app)
        partitioned = self.get_cookie_partitioned(app)
        samesite = self.get_cookie_samesite(app)
        httponly = self.get_cookie_httponly(app)

        # Add a "Vary: Cookie" header if the session was accessed at all.
        if session.accessed:
            response.vary.add("Cookie")

        # If the session is modified to be empty, remove the cookie.
        # If the session is empty, return without setting the cookie.
        if not session:
            if session.modified:
                response.delete_cookie(
                    name,
                    domain=domain,
                    path=path,
                    secure=secure,
                    partitioned=partitioned,
                    samesite=samesite,
                    httponly=httponly,
                )
                response.vary.add("Cookie")

            return

        if not self.should_set_cookie(app, session):
            return

        expires = self.get_expiration_time(app, session)
        val = self.get_signing_serializer(app).dumps(dict(session))  # type: ignore[union-attr]
        response.set_cookie(
            name,
            val,
            expires=expires,
            httponly=httponly,
            domain=domain,
            path=path,
            secure=secure,
            partitioned=partitioned,
            samesite=samesite,
        )
        response.vary.add("Cookie")

1. 會話(Session)的核心類

(1)SessionMixin

作用:為字典形式的 Session 提供擴(kuò)展屬性。

關(guān)鍵屬性

  • permanent:標(biāo)記 Session 是否為持久會話(基于 _permanent 鍵值)。
  • new:標(biāo)記 Session 是否為新創(chuàng)建的(默認(rèn)為 False)。
  • modified:標(biāo)記 Session 是否被修改(默認(rèn)為 True)。
  • accessed:標(biāo)記 Session 是否被訪問(默認(rèn)為 True)。

(2)SecureCookieSession

作用:基于簽名 Cookie 的默認(rèn) Session 實現(xiàn)(繼承自 CallbackDict 和 SessionMixin)。

特點

  • 自動跟蹤修改狀態(tài)(modified)和訪問狀態(tài)(accessed)。
  • 數(shù)據(jù)變更時會觸發(fā)回調(diào),確保狀態(tài)同步。
(3)NullSession

作用:當(dāng)未配置密鑰時,提供一個安全的空 Session 替代品。

行為

  • 允許讀取操作(返回空值)。
  • 拒絕寫入操作(直接拋出 RuntimeError)。

2. 會話接口(SessionInterface)

(1) 核心方法

  • open_session(app, request)
    功能:在請求開始時加載 Session。
    返回:需實現(xiàn)類字典接口 + SessionMixin 的對象,失敗時返回 None(觸發(fā) NullSession)。

  • save_session(app, session, response)
    功能:在請求結(jié)束時保存 Session 到響應(yīng)。
    注意:若 is_null_session(session) 為 True 則跳過。

(2) Cookie 配置方法

  • get_cookie_name():從配置 SESSION_COOKIE_NAME 獲取 Cookie 名稱。
  • get_cookie_domain():控制 Cookie 的 Domain 屬性(默認(rèn)無子域名共享)。
  • get_cookie_secure():根據(jù) SESSION_COOKIE_SECURE 決定是否僅 HTTPS 傳輸。
  • get_cookie_samesite():配置 SameSite 防 CSRF(Strict/Lax)。
  • get_cookie_partitioned():是否啟用 Partitioned 屬性(用于跨站 Cookie 隔離)。
(3) 輔助邏輯
  • should_set_cookie():決定是否設(shè)置 Set-Cookie 頭(基于修改狀態(tài)或 SESSION_REFRESH_EACH_REQUEST)。
  • make_null_session():生成 NullSession 實例。
  • is_null_session():檢查對象是否為無效 Session。

3. 默認(rèn)實現(xiàn)(SecureCookieSessionInterface)

(1) 安全特性

簽名機(jī)制:使用 itsdangerous.URLSafeTimedSerializer。

  • 支持密鑰輪換(SECRET_KEY_FALLBACKS)。
  • 可配置鹽值(salt)、哈希算法(如 sha1)和密鑰派生方式(如 hmac)。

數(shù)據(jù)序列化:通過 TaggedJSONSerializer 處理 Python 特殊類型(如 datetime)。

(2) 工作流程

讀取 Session

  • 從請求的 Cookie 中加載數(shù)據(jù)。
  • 驗證簽名和時間戳(防止篡改和過期會話)。
  • 失敗時返回空 Session。

保存 Session

  • 若 Session 為空且被修改 → 刪除 Cookie。
  • 若需更新(should_set_cookie 為 True)→ 寫入簽名后的數(shù)據(jù)到 Cookie。
  • 自動設(shè)置 Vary: Cookie 頭以適配緩存。

(3) 配置示例

app.config.update(
    SECRET_KEY="your-secret-key",
    SESSION_COOKIE_NAME="my_session",
    SESSION_COOKIE_SECURE=True,
    SESSION_COOKIE_SAMESITE="Lax",
    PERMANENT_SESSION_LIFETIME=timedelta(days=7),
)

4. 設(shè)計思想

  • 客戶端存儲:Session 數(shù)據(jù)默認(rèn)通過 Cookie 存儲(節(jié)省服務(wù)端資源)。
  • 安全優(yōu)先:所有數(shù)據(jù)簽名防篡改,且支持安全相關(guān)的 Cookie 屬性。
  • 靈活擴(kuò)展:通過實現(xiàn) SessionInterface 可輕松切換為服務(wù)端 Session(如 Redis 存儲)。

常見問題

  • 密鑰缺失:未設(shè)置 SECRET_KEY 時,所有 Session 操作將觸發(fā) NullSession 的異常。
  • 性能注意:Cookie 大小受限(通常不超過 4KB),復(fù)雜數(shù)據(jù)需考慮服務(wù)端存儲方案。

如果需要進(jìn)一步分析特定部分(如自定義 Session 存儲),可以深入探討具體實現(xiàn)方式。

到此這篇關(guān)于flask庫中sessions.py的使用小結(jié)的文章就介紹到這了,更多相關(guān)flask sessions.py內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評論