Author Archives: elwood

Настройка SSL для томката. Взгляд с 1000 метров.

Written by elwood

На неделе столкнулся с задачей сформировать подписанный сертификат SSL и установить его в JBoss AS. До этого там работал самоподписанный сертификат, сформировать который достаточно просто с использованием утилитки keytool. Эта программа входит в набор, поставляемый вместе с JDK, поэтому имеется у всех джаверов. Так как до меня достаточно долго доходило, что, собственно, требуется, попробую описать вкратце, что к чему.

Итак, нам нужно настроить SSL на томкате (в моем случае был JBoss, но там внутри все равно томкат, так что разницы нет). Что значит настроить SSL ? Это означает, что томкат должен уметь принимать входящие подключения на некий порт (обычно 443) по протоколу HTTPS. Для этого необходимо настроить коннектор, он настраивается в server.xml (в томкате его проще найти, в JBoss он упрятан глубоко, в один из деплоеров – WebDeployer). Чтобы настроить коннектор, нужно указать ему порт, некоторые дополнительные настройки, а также keystore + keystore password – собственно самая важная часть этого конфига.

Что же такое keystore ? Это просто файл, хранящий связки ключей и сертификаты. Файл может быть запаролен (поэтому для доступа к нему в конфиге прописывается keystore password). Для работы SSL необходима пара ключей (public + private) плюс сертификат, который доказывает подлинность этой пары ключей. Сертификат по сути представляет собой public-ключ, подписанный (см. цифровая подпись) одним из доверенных центров сертификации. Схема примерно такая – клиент при создании защищенного соединения смотрит сертификат, и либо доверяет ему (если он подписан авторитетным центром), либо выводит сообщение о недоверенном сертификате (что мы часто наблюдаем в браузерах). К слову, сертификат может быть подписан с помощью другого подписанного сертификата, таким образом, могут образовываться цепочки сертификатов, и в этом случае доверенным сертификатом должен быть хотя бы ROOT-сертификат. Ну и собственно эта пара ключей и цепочка сертификатов хранятся в keystore.

Есть 2 пути сделать себе keystore. Первый – самый простой – создать пару ключей и ей же самой подписать публичный ключ, получив сертификат. Это и есть самоподписанный сертификат. Его проблема в том, что нет причин доверять такому сертификату, поскольку любому человеку под силу создать такой сертификат. Такие сертификаты при использовании их на сайтах как раз и приводят к большим красным сообщениям о том, что де-сайт плохой и надо с него валить.

Второй – получение пары ключей с сертификатом, подписанным доверенным центром. Тут чуть сложнее, сначала опять же нужно сгенерить себе пару ключей и засунуть ее в свежесозданный keystore. Потом – создать запрос на получение сертификата (CSR-файл), он делается одной командой из созданного keystore. Дальше надо отправить этот файл в центр сертификации и дождаться выдачи сертификата. Центр сертификации выдаст нам файл, содержащий искомый сертификат и (при необходимости) цепочку доверенных сертификатов. Всё это добро мы импортируем в исходный keystore и.. вуаля, мы имеем настоящий подписанный доверенным центром SSL сертификат!

Пара технических деталей. Keystore представляет собой по сути сериализованную мультимапу alias -> objects[], где алиасом является некая строка, а object – либо keyentry, либо trusted cert entry. Названия говорят сами за себя. Следствие идентификации по алиасу – вы должны делать импорт полученных из центра сертификатов на тот же алиас, на который вы создавали keypair.

Парочка ссылок на толковое описание по шагам, что нужно делать (с командами для keytool):
Генерация ключей и создание запроса на получение сертификата
Инсталляция полученного сертификата в tomcat

Как работает UrlRewriteFilter

Written by elwood

Немного механики работы UrlRewriteFilter, который часто используется джаверами при создании приложений. И не только servlet-based, но еще и использующих портлеты. Исследовался конкретно JBoss portal 2.6.5, но всё это на 99% можно отнести к другим серверам приложений и сервлет-контейнерам.

1. Сначала исходный запрос (CoyoteRequest) обрабатывается томкатом, выделяя параметры, которые присутствуют в запросе. Допустим, запрос был такого вида:

http://localhost:8080/wiki/l/А?d=s&mmm=asd

Так вот, параметры d и mmm будут присутствовать в коллекции parameters вне зависимости от применённых маппингов.

2. После этого от запроса отрезаются параметры, и полученный URL попадает в фильтр URLRewrite.
Точнее, никто их не отрезает, естественно, но фильтр URLRewrite их попросту не учитывает.
Таким образом, на вход UrlRewrite filter поступает урл

http://localhost:8080/wiki/l/А

3. Найдя подходящий маппинг, UrlRewriteFilter применяет его к запросу, передавая в filterChain измененный запрос.
Следующие фильтры и итоговые сервлеты, а далее и портлет-контейнер, а потом и собственно портлеты увидят преобразованный запрос.

http://localhost:8080/wiki/?l=A

И тут мы видим, что первичные параметры d и mmm казалось бы утеряны, но не тут-то было ! При реврайтинге запроса UrlRewriteFilter сохранил их. Скорее всего, при инициализации замещающего запроса туда были сразу скопированы параметры исходного запроса, а потом уже дописаны те, которые получились при парсинге отреврайтенного запроса.

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

Заметьте, UrlRewriteFilter мапит только URL, а не целиком весь запрос с параметрами. Поэтому, если вам вдруг захочется сделать маппинг, который бы зависел от параметров, то у вас это не получится (предупреждаю, потому что сам как-то с этим столкнулся). UrlRewriteFilter будет попросту игнорировать параметры, поскольку на входе имеет только url. Но такая фича и не нужна, поскольку параметры, дописанные к реврайченным урлам, остаются в запросе, и вы можете попросту добавить логику обработки этих параметров в приложение.

Как проверить базовое знание SQL

Written by elwood

На днях вспомнил об одной задаче, которая мне попалась на работе года полтора назад.
В то время я еще не знал об sql-ex.ru и мои познания в SQL были достаточно слабыми, на уровне
простейших селектов с inner join’ами. И, как я помню тогда, я не смог решить эту задачу без
дополнительного подзапроса. Сейчас, после нескольких десятков решенных задач на sql-ex.ru я
без проблем с ней справился, что навело меня на мысль о том, что это – неплохой тест на знание
азов SQL. В отличие от более сложных задач, эта задачка не требует большого количества времени, но
покрывает почти всё, что требуется для среднего программера в вопросе знания SQL, особенно если решать её в уме. Сразу отпадают вопросы наподобие “что такое JOIN, чем LEFT JOIN отличается от INNER JOIN, как работает группировка” итд.

Итак, формулировка такова. Есть 2 таблицы – Collection и Item.

Необходимо сделать выборку
Collection.ID Collection.Name Collection.Count
тех коллекций, которые содержат меньше 5 элементов.
Вот и всё.

ДАЛЬШЕ СПОЙЛЕР, НЕ ЧИТАТЬ !!!111 (Я не научился пока скрывать в вордпрессе куски постов, но как только научусь – оформлю соответствующе).

Алгоритм решения задачи.
Ну ясно же – надо сделать JOIN, делаем JOIN, группируем по Collection.ID, выводим ID, Name, Count(*).
Черт. При группировке по ID нельзя вывести Name. Значит, надо либо завернуть полученную выборку в еще 1 запрос, либо дописать в группировку этот Name. Ок, работает. Только пустые коллекции не выводятся. Почему ? Ну ясно же – они не попадают в результат INNER JOIN’a, надо использовать LEFT JOIN. О, теперь получилось. Только коллекции-то пустые, а count(*) выводит единицу вместо нуля. Ах да, это же LEFT JOIN, он джойнит Collection.ID Collection.Name NULL NULL и count(*) дает единицу. Как поправить-то. Вычесть 1 нельзя, поскольку так можно запороть непустые коллекции. Надо как-то проверить, является ли коллекция пустой, и для нее вывести 0, а для остальных – count(*). Да у нас же справа NULL, можно их в какую-нибудь агрегатную функцию запихнуть, и сравнить с NULL’ом. Если NULL, значит, коллекция пустая, и для нее выводим 0, иначе – count(*). Эврика! Ну и добавляем HAVING. Пишем запрос:

SELECT
  c.ID, c.Name, CASE WHEN COUNT(*) = 1 AND MAX(i.CollectionID) IS NULL THEN 0 ELSE COUNT(*) END
FROM Collection c
LEFT JOIN Item i ON c.ID = i.CollectionID
GROUP BY c.ID, c.Name
HAVING COUNT(*) < 5