Java Чтение недекодированного URL-адреса из сервлета

Предположим, что у меня есть строка типа '=&?/;#+%', которая является частью моего URL-адреса, скажем так:

example.com/servletPath/someOtherPath/myString/something.html?a=b&c=d#asdf

где myString — указанная выше строка. Я закодировал критическую часть, поэтому URL выглядит как

example.com/servletPath/someOtherPath/%3D%26%3F%2F%3B%23%2B%25/something.html?a=b&c=d#asdf

Все идет нормально.

Когда я нахожусь в сервлете и читаю любой из request.getRequestURI(), request.getRequestURL() или request.getPathInfo(), возвращаемое значение уже декодировано, поэтому я получаю строку вроде

someOtherPath/=&?/;#+%/something.html?a=b&c=d#asdf

и я не могу отличить настоящие специальные символы от закодированных.

Я решил конкретную проблему, полностью запретив вышеуказанные символы, что работает в этой ситуации, но мне все еще интересно, есть ли способ получить некодированный URL-адрес в классе сервлета.

ЕЩЕ ОДНА ПРАВКА: когда я столкнулся с этой проблемой прошлым вечером, я был слишком устал, чтобы заметить, что происходит на самом деле, что еще более странно! У меня есть отображение сервлета, скажем, /servletPath/* после что я могу поместить все, что захочу, и получить ответ моего сервлета в зависимости от остальной части пути, кроме, когда в пути есть %2F. В этом случае запрос никогда не попадает в сервлет, и я получаю 404! Если я поставлю '/' вместо %2F, все будет работать нормально. Я использую Tomcat 6.0.14 на Java 1.6.0-04 в Linux.


person Slartibartfast    schedule 08.06.2009    source источник
comment
если строка уже декодирована, зачем в ней %2f?   -  person Mike Pone    schedule 08.06.2009
comment
Как выглядит возвращаемое значение и каким вы хотите его видеть? И это актуально? Я не могу сказать, в чем проблема.   -  person Michael Myers    schedule 08.06.2009
comment
Похоже на попытку декодировать незаконный и искаженный URL-адрес. Выход за пределы спецификации, как это, может вызвать кучу проблем. Можете ли вы изменить способ передачи данных? например перейти к публикации данных?   -  person Cheekysoft    schedule 09.06.2009
comment
Для тех, кто наткнется на это в будущем, проблема с %2F связана с мера безопасности CGI.   -  person jkitchen    schedule 25.06.2014


Ответы (5)


Между «%2F» и «/» есть принципиальная разница как для браузера, так и для сервера.

Спецификация HttpServletRequest говорит (без какой-либо логики, AFAICT):

  • getContextPath: не декодируется
  • getPathInfo: расшифровано
  • getPathTranslated: не расшифровано
  • getQueryString: не декодируется
  • getRequestURI: не декодируется
  • getServletPath: декодировано

Результат getPathInfo() должен декодироваться, но результат getRequestURI() не должен декодироваться. Если это так, ваш контейнер сервлета нарушает спецификацию (как правильно указали Воутер Кукертс и Франсуа Гравель). Какую версию Tomcat вы используете?

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

person jcsahnwaldt Reinstate Monica    schedule 30.06.2009

Если в декодированном URL есть %2F, это означает, что закодированный URL содержит %252F.

Поскольку %2F это /, почему бы просто не разделить "\/" и не беспокоиться о кодировании URL?

person Powerlord    schedule 08.06.2009

Согласно Javadoc, getRequestURI не должен декодировать строку. С другой стороны, getServletPath возвращает декодированную строку. Я проверил это локально с помощью Jetty, и он ведет себя так, как описано в документе.

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

person Francois Gravel    schedule 09.06.2009
comment
Вы частично правы. Когда у меня есть какой-то символ UTF-8, он остается некодированным, но специальные символы отсутствуют. Я работаю над Томкатом. - person Slartibartfast; 09.06.2009

Похоже, вы пытаетесь сделать что-то RESTy (используйте Джерси). Можете ли вы просто проанализировать начальную и конечную части URL-адреса, чтобы получить данные, которые вы ищете?

url.substring(startLength, url.length - endLength);

person stevedbrown    schedule 08.06.2009
comment
нет, у меня есть param1/param2/param3 и все они неизвестной длины. - person Slartibartfast; 09.06.2009

Обновление: в этом ответе изначально ошибочно указывалось, что '/' и '%2F' в пути всегда должны обрабатываться одинаково. На самом деле они разные, потому что путь представляет собой список сегментов, разделенных /.

Вам не нужно делать различие между закодированным и незакодированным символом в части пути URL-адреса. Внутри пути нет символов, которые могут иметь особое значение в URL-адресе. Например. «%2F» должен интерпретироваться так же, как «/», и браузер, получающий доступ к такому URL-адресу, может свободно заменять один другим по своему усмотрению. Делать разницу между ними — значит нарушать стандарт кодирования URL-адресов.

В полном URL-адресе вы должны различать экранированные и неэкранированные символы по разным причинам, в том числе:

  • Чтобы увидеть, где заканчивается часть пути. Потому что? закодированный в пути не должен рассматриваться как конец.
  • Внутри строки запроса. Поскольку часть значения параметра может содержать '&' или '=',...
  • Внутри пути '/' разделяет два сегмента, а '%2F' может содержаться внутри сегмента.

Java отлично справляется с первыми двумя случаями:

  • getPathInfo() который возвращает только декодированную часть пути
  • getParameter(String) для доступа к частям части запроса

С третьим случаем дело обстоит не так хорошо. Если вы хотите сделать разницу между '/' как разделением двух сегментов пути и '/' внутри сегмента пути (%2F), то вы не можете последовательно представить путь как одну декодированную строку. Вы можете представить его как одну закодированную строку (например, "foo/bar%2Fbaz") или как список декодированных сегментов (например, "foo", "bar/baz"). Но поскольку API getPathInfo() обещает сделать именно это (одну декодированную строку), у него нет другого выбора, кроме как рассматривать '/' и '%2F' как одно и то же.

Для обычных веб-приложений это нормально. Если вы находитесь в редком случае, когда вам действительно нужно что-то изменить, вы можете самостоятельно проанализировать URL-адрес, получив необработанную версию с помощью getRequestURI(). Если он дает декодированный URL-адрес, как вы утверждаете, это означает, что в используемой вами реализации сервлета есть ошибка.

person Wouter Coekaerts    schedule 09.06.2009
comment
Так что мне было плохо, что я думал, что между / и %2F есть разница, хотя по стандарту ее нет. Как я уже сказал, я пропустил проблему, удалив символы до того, как они попали в часть кодирования URL, что, я думаю, является единственным стандартным способом компиляции. - person Slartibartfast; 09.06.2009
comment
На самом деле, я считаю, что есть разница между / и %2F в пути. RFC3986 указывает, что путь представляет собой последовательность сегментов пути, разделенных /. Поэтому, если вы хотите, чтобы сегмент пути содержал символ косой черты, он должен быть закодирован как %2F. Об этом говорится, например, в статье Википедии о процентном кодировании. Насколько я понимаю, нормально иметь сервер, который использует это различие, и браузер, который не поддерживает это различие, будет сломан. - person Robert Tupelo-Schneck; 16.04.2012
comment
@RobertTupelo-Schneck Вы правы. Я только что отредактировал ответ, чтобы исправить это. - person Wouter Coekaerts; 09.05.2012