Чтение 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.
Решение
Между «%2F» и «/» существует фундаментальная разница как для браузера, так и для сервера.
В спецификации HttpServletRequest говорится (без какой-либо логики, AFAICT):
- получитьКонтекстПат:не декодирован
- получитьПатИнфо:декодированный
- getPathTranslated:не декодирован
- получитьQueryString:не декодирован
- getRequestURI:не декодирован
- getServletPath:декодированный
Результат getPathInfo() должен декодироваться, но результат getRequestURI() не должен быть расшифровано.Если это так, ваш контейнер сервлетов нарушает спецификацию (как правильно заметили Воутер Кукертс и Франсуа Гравель).Какую версию Tomcat вы используете?
Еще больше запутывает ситуацию то, что текущие версии Tomcat отклоняют пути, содержащие кодировки определенных специальных символов. по соображениям безопасности.
Другие советы
Если есть %2F
в декодированный URL, это означает закодированный URL-адрес содержится %252F
.
С %2F
является /
Почему бы просто не разделить "\/"
и не беспокоиться о кодировке URL?
Согласно Javadoc, getRequestURI не должен декодировать строку.С другой стороны, getServletPath возвращает декодированную строку.Я тестировал это локально с помощью Jetty, и он ведет себя так, как описано в документе.
Таким образом, в вашей ситуации может быть что-то еще, поскольку поведение, которое вы описываете, не соответствует документации Sun.
Кажется, вы пытаетесь сделать что-то RESTy (используйте Джерси).Можете ли вы просто проанализировать начальную и конечную части URL-адреса, чтобы получить искомые данные?
url.substring(startLength, url.length - endLength);
Обновлять: изначально в этом ответе ошибочно говорилось, что «/» и «%2F» в пути всегда следует рассматривать одинаково.На самом деле они разные, поскольку путь представляет собой список сегментов, разделенных /.
Вам не нужно делать разницу между закодированным и незакодированным символом в часть пути URL-адреса.Внутри пути нет символов, которые могли бы иметь особое значение в URL-адресе.Например.«%2F» должен интерпретироваться так же, как «/», и браузер, обращающийся к такому URL-адресу, может заменить один на другой по своему усмотрению.Разница между ними означает нарушение стандарта кодирования URL-адресов.
В полном URL-адресе необходимо различать экранированные и неэкранированные символы по разным причинам, в том числе:
- Чтобы увидеть, где заканчивается часть пути.Потому что?закодированный в пути, не должен рассматриваться как конец.
- Внутри запроса String.Поскольку часть значения параметра может содержать «&» или «=",...
- Внутри пути символ «/» разделяет два сегмента, а «%2F» может содержаться внутри сегмента.
Java прекрасно справляется с первыми двумя случаями:
getPathInfo()
который возвращает только часть пути, декодированнуюgetParameter(String)
для доступа к частям части запроса
С третьим случаем дело обстоит не так хорошо.Если вы хотите сделать разницу между «/» как разделением двух сегментов пути и «/» внутри сегмента пути (%2F), вы не сможете последовательно представить путь как одну декодированную строку.Вы можете представить его как одну закодированную строку (например, «foo/bar%2Fbaz») или как список декодированных сегментов (например, «foo», «bar/baz»).Но поскольку API getPathInfo() обещает сделать именно это (одну декодированную строку), у него нет другого выбора, кроме как рассматривать '/' и '%2F' как одно и то же.
Для обычных веб-приложений это вполне нормально.Если вы находитесь в том редком случае, когда вам действительно нужно что-то изменить, вы можете выполнить собственный анализ URL-адреса, получив необработанную версию с помощью getRequestURI()
.Если он дает URL-адрес, декодированный, как вы утверждаете, это означает, что в реализации сервлета, который вы используете, есть ошибка.