Можно ли получить доступ к сеансу (из aiohttp_session) в промежуточном программном обеспечении? - PullRequest
3 голосов
/ 06 июня 2019

Я настраиваю сервер aiohttp, используя aiohttp_session для хранения данных в EncryptedCookieStorage.Я использую его для хранения 7-дневного действующего токена, а также даты истечения срока действия и токена обновления.Я хочу, независимо от того, к какой конечной точке клиент обращается, чтобы проверить, нуждается ли токен (сохраненный в сеансе) в некотором обновлении.Выбор промежуточного программного обеспечения был довольно очевиден.

Проблема в том, что когда я звоню await aiohttp_session.get_session(request), я получаю хороший RuntimeError с просьбой настроить промежуточное ПО aiohttp_session на aiohttp.web.Application.Я предполагаю, что мое специальное промежуточное ПО называлось до , которое обрабатывало загрузку сеанса, поэтому сеанс еще не доступен.Я искал какую-то "приоритетную" систему в отношении промежуточного программного обеспечения, но ничего не нашел.

Мой сервер настроен в файле main.py, например:

def main():
    app = web.Application()
    middleware.setup(app)
    session_key = base64.urlsafe_b64decode(fernet.Fernet.generate_key())
    aiohttp_session.setup(app, EncryptedCookieStorage(session_key))
    # I have tried swapping the two setup functions

    web.run_app(app)


if __name__ == '__main__':
    main()

Гдеmiddleware.setup() находится в отдельном пакете, в __init__.py:

# For each python file in the package, add it's middleware function to the app middlewares
def setup(app):
    for filename in listdir('middleware'):
        if filename[-2:] == 'py' and filename[:2] != '__':
            module = __import__('rpdashboard.middleware.' + filename[:-3], fromlist=['middleware'])
            app.middlewares.append(module.middleware)

И, наконец, промежуточное программное обеспечение, в которое я хочу получить сеанс:

@web.middleware
async def refresh_token_middleware(request, handler):
    session = await get_session(request)
    if session.get('token'):
        pass  # To be implemented ...

    return await handler(request)


middleware = refresh_token_middleware

Проблемы с выполнениемздесь:

# From aiohttp_session
async def get_session(request):
    session = request.get(SESSION_KEY)
    if session is None:
        storage = request.get(STORAGE_KEY)
        if storage is None:
            # This is raised
            raise RuntimeError(
                "Install aiohttp_session middleware "
                "in your aiohttp.web.Application")

Как я говорил ранее, похоже, что сеанс не предназначен для доступа в промежуточном программном обеспечении и еще не загружен.Так, как я мог бы препятствовать тому, чтобы мое пользовательское промежуточное ПО запустилось до загрузки сеанса?Или, может быть, просто запустить вручную промежуточное программное обеспечение aiohttp_session самостоятельно?Это вообще возможно?

Ответы [ 2 ]

1 голос
/ 04 июля 2019

Да, компоненты промежуточного программного обеспечения, добавленные в приложение в правильном порядке , могут обращаться к хранилищу сеансов, установленному промежуточным программным обеспечением сеанса.

Документация aiohttp охватывает порядок приоритетов для компонентов промежуточного программного обеспечения в их Промежуточном программном обеспечении раздел :

Внутренне, один обработчик запросов создается путем применения цепочки промежуточного программного обеспечения к исходному обработчику в обратном порядке и вызывается RequestHandler как обычный обработчик .

Далее они используют пример, чтобы продемонстрировать, что это значит. Таким образом, они используют два компонента промежуточного программного обеспечения, которые сообщают о своем входе и выходе и добавляют их в список app.middlewares в следующем порядке:

... middlewares=[middleware1, middleware2]

Этот порядок дает следующий результат:

Middleware 1 called
Middleware 2 called
Handler function called
Middleware 2 finished
Middleware 1 finished

Таким образом, входящий запрос передается по другому промежуточному программному обеспечению в том же порядке, в котором они добавляются в список app.middlewares .

Далее, aiohttp_session также документирует, как они добавляют свое промежуточное программное обеспечение сеанса, в записи API для aiohttp_session.setup():

Функция ярлыка для:

app.middlewares.append(session_middleware(storage))

Таким образом, их компонент промежуточного программного обеспечения добавляется в конец списка. В соответствии с вышесказанным это означает, что все, что требует доступа к сеансу, должно идти после этого компонента промежуточного программного обеспечения.

Все, что делает промежуточное программное обеспечение сеанса, это добавляет хранилище к запросу под ключом aiohttp_session.STORAGE_KEY; это делает сеансы доступными для любых последующих компонентов промежуточного программного обеспечения, которые следуют за ним. Компонентам промежуточного программного обеспечения не нужно делать ничего особенного, кроме добавления после промежуточного программного обеспечения сеанса, и оставить объект хранилища добавленным к запросу на месте. Объект запроса предназначен для совместного использования данных между компонентами .

Ваш код помещает все компоненты промежуточного программного обеспечения перед компонентом промежуточного программного обеспечения сеанса:

middleware.setup(app)
# ...
aiohttp_session.setup(app, EncryptedCookieStorage(session_key))

Это дает вам заказ [..., refresh_token_middleware, ..., session_middleware], и ваше промежуточное ПО не может получить доступ к любой информации о сеансе.

Так что вы должны поменять местами заказ; сначала вызовите aiohttp_session.setup() , а затем добавьте свои собственные компоненты:

aiohttp_session.setup(app, EncryptedCookieStorage(session_key))
middleware.setup(app)

Если у вас все еще есть проблемы с доступом к хранилищу сеансов, это означает, что один из промежуточных компонентов промежуточного программного обеспечения снова удаляет информацию о хранилище сеансов .

Вы можете использовать следующую фабрику промежуточного программного обеспечения в разных местах, чтобы сообщить о наличии хранилища сеансов, чтобы помочь вам отладить это:

from aiohttp import web
from aiohttp_session import STORAGE_KEY

COUNTER_KEY = "__debug_session_storage_counter__"
_label = {
    False: "\x1b[31;1mMISSING\x1b[0m",
    True: "\x1b[32;1mPRESENT\x1b[0m",
}

def debug_session_storage(app):
    pre = nxt = ""
    if app.middlewares:
        previous = app.middlewares[-1]
        name = getattr(previous, "__qualname__", repr(previous))
        pre = f" {name} ->"
        nxt = f" {name} <-"

    @web.middleware
    async def middleware(request, handler):
        counter = request.get(COUNTER_KEY, -1) + 1
        request[COUNTER_KEY] = counter
        found = STORAGE_KEY in request
        indent = " " * counter
        print(f"{indent}-{pre} probe#{counter} - storage: {_label[found]}")
        try:
            return await handler(request)
        finally:
            print(f"{indent}-{nxt} probe#{counter} - done")

    app.middlewares.append(middleware)

Если вы вставите это между каждым элементом промежуточного программного обеспечения, которое вы добавите, вы сможете выяснить, если и где хранилище сеанса теряется:

def setup(app):
    # start with a probe
    debug_session_storage(app)

    for filename in listdir('middleware'):
        if filename[-2:] == 'py' and filename[:2] != '__':
            module = __import__('rpdashboard.middleware.' + filename[:-3], fromlist=['middleware'])

            app.middlewares.append(module.middleware)

            # Add debug probe after every component
            debug_session_storage(app)

Это должно сказать вам

  • какой компонент промежуточного программного обеспечения предшествовал каждому исследованию
  • если имеется хранилище сеанса, с использованием зеленого и красного цветов ANSI, чтобы его было легко обнаружить
  • если есть, полностью сбросили запрос; если отсчет проб начинается снова с 0, тогда что-то очищает не только сеансовый ключ, но и счётчик проб!
1 голос
/ 03 июля 2019

Вы сами меняете порядок. Код должен быть таким

def main():
    app = web.Application()
    session_key = base64.urlsafe_b64decode(fernet.Fernet.generate_key())
    aiohttp_session.setup(app, EncryptedCookieStorage(session_key))
    # I have tried swapping the two setup functions
    middleware.setup(app)

    web.run_app(app)

Если вы посмотрите на код aiohttp_session.setup

https://github.com/aio-libs/aiohttp-session/blob/master/aiohttp_session/init.py

def setup(app, storage):
    """Setup the library in aiohttp fashion."""

    app.middlewares.append(session_middleware(storage))

Как видите, промежуточное ПО добавлено в эту функцию. Добавление промежуточного программного обеспечения до middleware.setup(app) делает сеанс по-прежнему недоступным для запроса

...