# -*- coding: utf-8 -*-"""flask.sessions~~~~~~~~~~~~~~Implements cookie based sessions based on itsdangerous.:copyright: © 2010 by the Pallets team.:license: BSD, see LICENSE for more details."""import hashlibimport warningsfrom collections import MutableMappingfrom datetime import datetimefrom itsdangerous import BadSignature, URLSafeTimedSerializerfrom werkzeug.datastructures import CallbackDictfrom flask.helpers import is_ip, total_secondsfrom flask.json.tag import TaggedJSONSerializerclass SessionMixin(MutableMapping):"""Expands a basic dictionary with session attributes."""@propertydef permanent(self):"""This reflects the ``'_permanent'`` key in the dict."""return self.get('_permanent', False)@permanent.setterdef permanent(self, value):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 = Trueclass SecureCookieSession(CallbackDict, 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 asession 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 = Falsedef __init__(self, initial=None):def on_update(self):self.modified = Trueself.accessed = Truesuper(SecureCookieSession, self).__init__(initial, on_update)def __getitem__(self, key):self.accessed = Truereturn super(SecureCookieSession, self).__getitem__(key)def get(self, key, default=None):self.accessed = Truereturn super(SecureCookieSession, self).get(key, default)def setdefault(self, key, default=None):self.accessed = Truereturn super(SecureCookieSession, self).setdefault(key, default)class NullSession(SecureCookieSession):"""Class used to generate nicer error messages if sessions are notavailable. Will still allow read-only access to the empty sessionbut fail on setting."""def _fail(self, *args, **kwargs):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 = _faildel _failclass SessionInterface(object):"""The basic interface you have to implement in order to replace thedefault session interface which uses werkzeug's securecookieimplementation. The only methods you have to implement are:meth:`open_session` and :meth:`save_session`, the others haveuseful defaults which you don't need to change.The session object returned by the :meth:`open_session` method has toprovide a dictionary like interface plus the properties and methodsfrom the :class:`SessionMixin`. We recommend just subclassing a dictand adding that mixin::class Session(dict, SessionMixin):passIf :meth:`open_session` returns ``None`` Flask will call into:meth:`make_null_session` to create a session that acts as replacementif the session support cannot work because some requirement is notfulfilled. The default :class:`NullSession` class that is createdwill complain that the secret key was not set.To replace the session interface on an application all you have to dois to assign :attr:`flask.Flask.session_interface`::app = Flask(__name__)app.session_interface = MySessionInterface().. 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.10pickle_based = Falsedef make_null_session(self, app):"""Creates a null session which acts as a replacement object if thereal session support could not be loaded due to a configurationerror. This mainly aids the user experience because the job of thenull session is to still support lookup without complaining butmodifications are answered with a helpful error message of whatfailed.This creates an instance of :attr:`null_session_class` by default."""return self.null_session_class()def is_null_session(self, obj):"""Checks if a given object is a null session. Null sessions arenot 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_domain(self, app):"""Returns the domain that should be set for the session cookie.Uses ``SESSION_COOKIE_DOMAIN`` if it is configured, otherwisefalls back to detecting the domain based on ``SERVER_NAME``.Once detected (or if not set at all), ``SESSION_COOKIE_DOMAIN`` isupdated to avoid re-running the logic."""rv = app.config['SESSION_COOKIE_DOMAIN']# set explicitly, or cached from SERVER_NAME detection# if False, return Noneif rv is not None:return rv if rv else Nonerv = app.config['SERVER_NAME']# server name not set, cache False to return none next timeif not rv:app.config['SESSION_COOKIE_DOMAIN'] = Falsereturn None# chop off the port which is usually not supported by browsers# remove any leading '.' since we'll add that laterrv = rv.rsplit(':', 1)[0].lstrip('.')if '.' not in rv:# Chrome doesn't allow names without a '.'# this should only come up with localhost# hack around this by not setting the name, and show a warningwarnings.warn('"{rv}" is not a valid cookie domain, it must contain a ".".'' Add an entry to your hosts file, for example'' "{rv}.localdomain", and use that instead.'.format(rv=rv))app.config['SESSION_COOKIE_DOMAIN'] = Falsereturn Noneip = is_ip(rv)if ip:warnings.warn('The session cookie domain is an IP address. This may not work'' as intended in some browsers. Add an entry to your hosts'' file, for example "localhost.localdomain", and use that'' instead.')# if this is not an ip and app is mounted at the root, allow subdomain# matching by adding a '.' prefixif self.get_cookie_path(app) == '/' and not ip:rv = '.' + rvapp.config['SESSION_COOKIE_DOMAIN'] = rvreturn rvdef get_cookie_path(self, app):"""Returns the path for which the cookie should be valid. Thedefault implementation uses the value from the ``SESSION_COOKIE_PATH``config var if it's set, and falls back to ``APPLICATION_ROOT`` oruses ``/`` if it's ``None``."""return app.config['SESSION_COOKIE_PATH'] \or app.config['APPLICATION_ROOT']def get_cookie_httponly(self, app):"""Returns True if the session cookie should be httponly. Thiscurrently just returns the value of the ``SESSION_COOKIE_HTTPONLY``config var."""return app.config['SESSION_COOKIE_HTTPONLY']def get_cookie_secure(self, app):"""Returns True if the cookie should be secure. This currentlyjust returns the value of the ``SESSION_COOKIE_SECURE`` setting."""return app.config['SESSION_COOKIE_SECURE']def get_cookie_samesite(self, app):"""Return ``'Strict'`` or ``'Lax'`` if the cookie should use the``SameSite`` attribute. This currently just returns the value ofthe :data:`SESSION_COOKIE_SAMESITE` setting."""return app.config['SESSION_COOKIE_SAMESITE']def get_expiration_time(self, app, session):"""A helper method that returns an expiration date for the sessionor ``None`` if the session is linked to the browser session. Thedefault implementation returns now + the permanent sessionlifetime configured on the application."""if session.permanent:return datetime.utcnow() + app.permanent_session_lifetimedef should_set_cookie(self, app, session):"""Used by session backends to determine if a ``Set-Cookie`` headershould be set for this session cookie for this response. If the sessionhas been modified, the cookie is set. If the session is permanent andthe ``SESSION_REFRESH_EACH_REQUEST`` config is true, the cookie isalways 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, request):"""This method has to be implemented and must either return ``None``in case the loading failed because of a configuration error or aninstance of a session object which implements a dictionary likeinterface + the methods and attributes on :class:`SessionMixin`."""raise NotImplementedError()def save_session(self, app, session, response):"""This is called for actual sessions returned by :meth:`open_session`at the end of the request. This is still called during a requestcontext so if you absolutely need access to the request you can dothat."""raise NotImplementedError()session_json_serializer = TaggedJSONSerializer()class SecureCookieSessionInterface(SessionInterface):"""The default session interface that stores sessions in signed cookiesthrough 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 sha1digest_method = staticmethod(hashlib.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_serializersession_class = SecureCookieSessiondef get_signing_serializer(self, app):if not app.secret_key:return Nonesigner_kwargs = dict(key_derivation=self.key_derivation,digest_method=self.digest_method)return URLSafeTimedSerializer(app.secret_key, salt=self.salt,serializer=self.serializer,signer_kwargs=signer_kwargs)def open_session(self, app, request):s = self.get_signing_serializer(app)if s is None:return Noneval = request.cookies.get(app.session_cookie_name)if not val:return self.session_class()max_age = total_seconds(app.permanent_session_lifetime)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, session, response):domain = self.get_cookie_domain(app)path = self.get_cookie_path(app)# 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(app.session_cookie_name,domain=domain,path=path)return# Add a "Vary: Cookie" header if the session was accessed at all.if session.accessed:response.vary.add('Cookie')if not self.should_set_cookie(app, session):returnhttponly = self.get_cookie_httponly(app)secure = self.get_cookie_secure(app)samesite = self.get_cookie_samesite(app)expires = self.get_expiration_time(app, session)val = self.get_signing_serializer(app).dumps(dict(session))response.set_cookie(app.session_cookie_name,val,expires=expires,httponly=httponly,domain=domain,path=path,secure=secure,samesite=samesite)
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。