Исключение, когда публикация составной формы, прерванная по истечении времени ожидания сеанса, приводит к

#java #spring #spring-security #session-timeout #multipartform-data

#java #spring #spring-безопасность #тайм-аут сеанса #составная форма-данные

Вопрос:

У нас есть Spring security с CAS (я не думаю, что CAS является проблемой).

Проблема заключается НЕ в тайм-ауте сеанса, а в том, как этот тайм-аут обрабатывается.

  1. запрос формы: GET /someform
  2. заполните составную форму
  3. перезапустите сервер или удалите JSESSIONID
  4. отправьте форму: POST / someform (с составными данными)

  5. пользователь перенаправляется на экран входа в систему

  6. после входа в систему пользователь перенаправляется на форму: GET / someform
  7. spring пытается повторно опубликовать сохраненную форму (я думаю, что она использует DefaultSavedRequest)
  8. он пытается вызвать функцию контроллера, сопоставленную с: POST / someform, но запрос не является составным
  9. мы получаем исключение:

Не удалось вызвать метод обработчика [public org.springframework.web.servlet.ModelAndView com.xxx.xxx.XXXController.xxxPost(org.springframework.web.multipart.MultipartHttpServletRequest)]; вложенным исключением является java.lang.Исключение IllegalStateException: Текущий запрос не относится к типу org.springframework.web.multipart.MultipartHttpServletRequest: com.secondmarket.web.UrlLowerCaseFilter$LowerCaseUrlServletRequestWrapper@77fb58b6

Это код, который сохраняет запрос в сеанс при AccessDeniedException, он находится в HttpSessionRequestCache (вызывается ExceptionTranslationFilter):

 public void saveRequest(HttpServletRequest request, HttpServletResponse response) {
    if (!justUseSavedRequestOnGet || "GET".equals(request.getMethod())) {
        DefaultSavedRequest savedRequest = new DefaultSavedRequest(request, portResolver);

        if (createSessionAllowed || request.getSession(false) != null) {
            // Store the HTTP request itself. Used by AbstractAuthenticationProcessingFilter
            // for redirection after successful authentication (SEC-29)
            request.getSession().setAttribute(WebAttributes.SAVED_REQUEST, savedRequest);
            logger.debug("DefaultSavedRequest added to Session: "   savedRequest);
        }
    }

}
  

Как я могу перезаписать HttpSessionRequestCache или ExceptionTranslationFilter, чтобы НЕ сохранять запрос, если это составной запрос?

Ответ №1:

Решена проблема путем изменения подписи метода контроллера.

Ранее MultipartHttpServletRequest был в сигнатуре метода. Когда spring вернулся после обхода входа в систему, он попытался вызвать этот метод с помощью обычного HttpServletRequest и потерпел неудачу.

 @RequestMapping(value = "/xxx", method = RequestMethod.POST)
public ModelAndView doAmlCheckPost(MultipartHttpServletRequest req) {
    UserInfo currentUserInfo = UserInfo.getCurrentUserInfo(req);

    MultipartFile someFile = req.getFile("someFile");
  

Исправление заключается в использовании обычного сопоставления запросов и получении файлов по URL. Если запрос не является экземпляром MultipartHttpServletRequest — перенаправление на метод GET, который повторно отображает форму

 @RequestMapping(value = "/xxx", method = RequestMethod.POST)
public ModelAndView doAmlCheckMultipartPost(HttpServletRequest req, @RequestParam(value = "someFile", required = false) MultipartFile someFile) {

    if(!(req instanceof MultipartHttpServletRequest)){
        return "redirect:/xxx";
    }
  

Таким образом, поток теперь выглядит следующим образом:

  1. запрос формы: GET /someform
  2. заполните составную форму
  3. перезапустите сервер или удалите JSESSIONID
  4. отправьте форму: POST / someform (с составными данными), ЧТО ЗДЕСЬ ПРОИСХОДИТ, ЗАПРОС СОХРАНЯЕТСЯ В СЕАНСЕ, НО НЕТ СПОСОБА СОХРАНИТЬ СОСТАВНЫЕ ДАННЫЕ, ОНИ ДВОИЧНЫЕ И НЕ МОГУТ БЫТЬ СЕРИАЛИЗОВАНЫ
  5. пользователь перенаправляется на экран входа в систему
  6. после входа в систему пользователь перенаправляется на форму: GET / someform
  7. spring пытается повторно опубликовать сохраненную форму (я думаю, что она использует DefaultSavedRequest) SPRING ТЕПЕРЬ ВЫЗЫВАЕТ МЕТОД POST С HttpServletRequest, а не MultipartHttpServletRequest. Мы обнаруживаем, что это не MultipartHttpServletRequest и перенаправляем на страницу ПОЛУЧЕНИЯ, повторно отображая форму пользователю