Запросы POST Flask-Login превращаются в запросы GET после входа в систему

Я использую flask-login для обработки аутентификации для своего приложения, которое представляет собой API, который ожидает заголовки базовой авторизации HTTP как часть каждого запроса (чтобы пользователь мог войти в систему и сделать запрос, не беспокоясь о сеансах, файлах cookie или необходимости делать вход и запрос отдельными шагами).

Мои запросы работают следующим образом:

POST /api/group/48
GET /login?next=%2Fapi%2Fgroup%2F48
GET /api/group/48 

То есть запрос POST к /api/group/48 перехватывается и перенаправляется на конечную точку /login (как и ожидалось). То, что происходит в /login, не является интерактивным — оно берет заголовок Basic Authorization и регистрирует пользователя.

После завершения входа клиент перенаправляется обратно на /api/group/48, но на этот раз в виде запроса GET, а не POST. И в этом приложении конечная точка /api/group/48 ожидает только данные POST, поэтому она умирает с ошибкой 405 (метод не разрешен).

Это ожидаемое поведение flask-login? Как я могу передать его через запрос POST в том виде, в котором он был отправлен изначально? (или, в качестве альтернативы, я должен использовать какую-то другую архитектуру, чтобы перенаправление на /login, а затем обратно на /api/group/48 не происходило и данные POST не терялись?)

Я не включал код, так как не думаю, что это проблема, связанная с кодом. Но если выяснится, что я делаю что-то не так, я могу опубликовать пример кода.

Спасибо всем, кто может помочь.


person David White    schedule 13.07.2016    source источник
comment
Было бы сложно попытаться ответить на ваш вопрос без примера кода. Не могли бы вы включить минимальный пример (по возможности работающий) проблемы?   -  person Quint    schedule 13.07.2016
comment
Я не верю, что протокол HTTP допускает такое перенаправление. Как только базовая авторизация завершится, я думаю, что следующим запросом в цикле будет запрос GET.   -  person Wayne Werner    schedule 13.07.2016


Ответы (2)


Это больше, чем перенаправление (что также может быть проблематичным для некоторых браузеров/клиентов). Ваш клиент должен будет запомнить текст первоначального запроса и повторно опубликовать его позже. В спецификации HTTP такого механизма нет.

Что вы можете сделать, так это запомнить запрос (в сеансе на что-то похожее), затем выполнить перенаправление, а затем обработать сохраненный запрос. Я не думаю, что здесь есть ярлыки.

person frnhr    schedule 13.07.2016

Если вам действительно нужно перенаправить как POST, вы можете сделать это. Предположим, у вас есть представление с таким перенаправлением (без зазрения совести взято из документов flask-login):

@app.route('/login', methods=['GET', 'POST'])
def login():
    # Here we use a class of some kind to represent and validate our
    # client-side form data. For example, WTForms is a library that will
    # handle this for us, and we use a custom LoginForm to validate.
    form = LoginForm()
    if form.validate_on_submit():
        # Login and validate the user.
        # user should be an instance of your `User` class
        login_user(user)

        flask.flash('Logged in successfully.')

        next = flask.request.args.get('next')
        # next_is_valid should check if the user has valid
        # permission to access the `next` url
        if not next_is_valid(next):
            return flask.abort(400)

        return flask.redirect(next or flask.url_for('index'))
    return flask.render_template('login.html', form=form)

Затем, поскольку этот ответ цитирует Википедию:

Многие веб-браузеры реализовали этот код таким образом, что нарушили этот стандарт, изменив тип запроса нового запроса на GET, независимо от типа, использованного в исходном запросе (например, POST). 1. По этой причине в HTTP/1.1 (RFC 2616) добавлены новые коды состояния 303 и 307 для устранения неоднозначности между двумя вариантами поведения. , при этом 303 предписывает изменить тип запроса на GET, а 307 сохраняет тип запроса в исходном виде.

Итак, ваш ответ - изменить перенаправление на flask.redirect(next or flask.url_for('index'), code=307)

Также обратите внимание, что вам, вероятно, потребуется перенаправить на представление login как POST, чтобы оно заработало. Вы можете создать свой собственный неавторизованный обработчик:

@login_manager.unauthorized_handler
def unauthorized():
    # do stuff
    return a_response
person valignatev    schedule 13.07.2016
comment
Это интересная идея, valentjedi, но как заставить перенаправление на представление login использовать POST? Переопределить LoginManager.unauthorized_handler ? - person David White; 13.07.2016
comment
Почти да, я обновлю свой ответ - person valignatev; 13.07.2016