Вы всегда ДЕЛАЕТЕ РЕДИРЕКТ после ПУБЛИКАЦИИ?Если да, то как вам это удается?

StackOverflow https://stackoverflow.com/questions/1083357

  •  23-08-2019
  •  | 
  •  

Вопрос

Допустим, вы отправляете форму, которая влияет на вашу базу данных (добавление записей / их удаление / обновление), и вот как выглядит ваш запрос:

ПУБЛИКАЦИЯ / приложение / действие = обновление

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

Ответ.sendRedirect /приложение/ действие=главная страница

Это работает на удивление хорошо.Пользователю отправляется перенаправление после ПУБЛИКАЦИИ, поэтому, даже если пользователь попытается обновить страницу, нажав F5, все в порядке.Однако это не сработает, если вы сделали это:

RequestDispatcher.forward(/приложение/действие=главная страница)

Учитывая, что существует сценарий, в котором вам приходится отображать различные виды сообщений об ошибках / успехах после завершения обновления, вы, скорее всего, выполняете пересылку после публикации.В таком сценарии, как избежать повторения действий по обновлению дважды?

Я нахожу довольно забавным, что многие защищенные сайты (банки) / платежные шлюзы, как правило, информируют пользователя, размещая текст на экране, например "Пожалуйста, не нажимайте кнопки назад / обновить".

Неужели нет лучшего способа справиться с этим?Кроме просьбы к пользователю не нажимать эти кнопки?Когда я в последний раз проверял, там было нечто, называемое "Вертикальный кэш ответов".Фильтр, который определит уникальность вашего запроса в сеансе и попытается отправить кэшированный ответ, если запрос дублируется.Есть ли какие-нибудь более простые способы решения этой классической проблемы?

Вот ссылка на решение для вертикального кэша ответов, о котором я говорил: http://www.fingo.info/en/articles/_1.html.Я, однако, не уверен относительно того, насколько хорошо это действительно работает.

Это было полезно?

Решение

Одна из моих мыслей заключается в том, чтобы внедрить уникальный идентификатор (возможно, случайную строку) в качестве скрытого поля формы в форму, которая отправляется ПОСЛЕ отправки.Строка идентификатора может быть помещена в базу данных как "идентификатор транзакции".Теперь, когда вы переходите к обновлению базы данных, сначала проверьте, существует ли существующая запись с отправленным идентификатором транзакции, и если да, предположите, что это дубликат, и не меняйте базу данных.

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

Редактировать:как указано в комментариях, хранение идентификаторов транзакций в базе данных может занимать много места, но если это проблема, вы могли бы сохранить в памяти кэш всех идентификаторов транзакций, обработанных за последние 5 минут / 1 час / 1 день / что угодно.Это должно сработать, если только вам не противостоит решительный хакер...

Другие советы

Да, я считаю, что вы должны перенаправлять после ПУБЛИКАЦИИ, за исключением запросов API.Без этого вам не только придется беспокоиться о получении дублирующихся записей, когда пользователь использует кнопку "Назад", но и браузер будет выдавать пользователю раздражающие диалоги при попытке использовать кнопку "Назад".

Response.sendRedirect работает на практике, но с технической точки зрения это отправка неправильного кода HTTP-ответа для этой цели.sendRedirect отправляет 302, но правильный код, используемый для преобразования POST в GET, равен 303.(однако большинство браузеров будут обрабатывать 302 точно так же, как 303, если они получат его в ответ на СООБЩЕНИЕ)

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

Иногда приятно иметь сообщение о состоянии, чтобы еще больше подчеркнуть тот факт, что произошло действие.Простой способ сделать это - создать общий параметр для ваших представлений, который при установке будет отображать сообщение о завершении действия.например:

/widget?id=12345&msg=Widget+modified.

Здесь параметр "msg" содержит сообщение "Виджет изменен".Единственным недостатком такого подхода является то, что вредоносные сайты могут передавать вашим пользователям сбивающие с толку сообщения.например:

/account?msg=Foo+Corp.+hates+you.

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

Лучшее решение для решения проблемы показа сообщений о состоянии пользователям после ПУБЛИКАЦИИ с ЦЕЛЬЮ ПОЛУЧЕНИЯ перенаправления - это использовать пользовательские сеансы.

Как

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

userSession.put("success_messages", new HashSet<String>(){"Success", "Check your account balance"});
userSession.put("warning_messages", new HashSet<String>(){"Your account balance is low. Recharge immediately"});

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

Я нахожу довольно забавным, что многие защищенные сайты (банки) / платежные шлюзы, как правило, информируют пользователя, размещая текст на экране, например "Пожалуйста, не нажимайте кнопки назад / обновить".

некоторые люди считают, что лучше "отключить все события возврата, обновления на этих важных страницах".;Я не уверен, хорошо это или нет.

Но ваше адресованное решение "вертикальный кэш ответов" звучит мило

Это немного неочевидно, но:

  • создайте объект с ключом в пользовательском сеансе.
  • значение - это запрос + java Future для результата
  • немедленно вернитесь с перенаправлением на стороне клиента.
  • пока обрабатывается перенаправление на стороне клиента, попросите рабочий поток поработать над получением ответа.

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

Альтернатива состоит в том, чтобы заставить пользователя болезненно осознавать, сколько времени занимает работа с базой данных.

Обновление для системы безопасности (24 января 2011 ) :

Ключ уязвим для атаки, поскольку он является частью ответа клиенту, поэтому

  1. Сгенерируйте случайный ключ
  2. Используйте идентификатор сеанса пользователя в качестве соли для создания SHA-1
  3. Храните как случайный ключ, так и SHA-1 в базе данных с помощью (, ) в качестве первичного ключа.(никакой отдельной индексации по СЛУЧАЙНОМУ ключу.
  4. Используйте как RANDOMKEY, так и SHA-1 в качестве поиска по базе данных.
  5. Не сохраняйте идентификатор сеанса (избегайте проблем с конфиденциальностью, связанных с возможностью сопоставления многих записей одному и тому же пользователю).
  6. Срок действия результатов истекает через 2-3 дня.( Позволяет выполнять очистку ежедневным пакетным заданием и избегает создания проблем для пользовательских сеансов, которые длятся недолго )

Этот метод требует, чтобы любой хакер знал как идентификатор сеанса, так и случайный ключ.

Такой подход может показаться излишним, но защищенный от перенаправления механизм может быть использован в таких ситуациях, как сброс пароля.

Если вы работаете со сценариями на стороне сервера Java , а также используете struts 2 , то вы ссылаетесь на эту ссылку , в которой говорится об использовании токена .

http://www.xinotes.org/notes/note/369/

Токен должен быть сгенерирован и сохранен в сеансе для начального рендеринга страницы, когда запрос отправляется вместе с токеном в первый раз , в действии struts запустите поток с именем потока в качестве идентификатора токена и запустите логику, которую запросил клиент, когда клиент снова отправит тот же запрос, проверьте, все ли еще выполняется поток (thread.getcurrentthread().interrupted), если все еще выполняется, затем отправьте клиенту перенаправление 503.

Пожалуйста, посмотрите на ExecuteAndWaitInterceptor struts 2code, логика этого в сочетании с токеном поможет выполнить быстрый щелчок

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top