Общее·количество·просмотров·страницы

Java Dev Notes - разработка на Java (а также на JavaScript/Python/Flex и др), факты, события из АйТи

Показаны сообщения с ярлыком tomcat. Показать все сообщения
Показаны сообщения с ярлыком tomcat. Показать все сообщения

пятница, 9 июля 2010 г.

Подключение SSL к Томкату

Допустим, что нам в веб-приложении нужно иметь несколько URL, которые доступны только по HTTPS. Это может быть работа с любыми чувствительными данными: платежи, почта, и т. п. В основном, это, конечно, финансовые данные. Вот об этом и будет рассказано в этом посте.

По мотивам Setting Up SSL on Tomcat In 3 Easy Steps

Нужно сделать следующее:

  1. Сгенерировать ключ

  2. Настроить Tomcat для использования этого ключа

  3. Настроить веб-приложение на работу по HTTPS

Генерация ключа



Используем утилиту keytool:

keytool -genkey -alias jdevnotes -keypass jdevpasswd -keystore jdevnotes.keystore.bin -storepass jdevpasswd

Эта команда генерирует ключ с алиасом jdevnotes, который сохраняется в файл (keystore, хранилище ключей) jdevnotes.keystore.bin. Также задается пароль на ключ и на хранилище в целом (пароли одинаковые - jdevpasswd).

Немножко подробнее о ключах: Все ключи хранятся в хранилище ключей (файл jdevnotes.keystore.bin). Каждое хранилище может содержать множество ключей. Доступ к ключу осуществляется по алиасу. Алиасы - регистро-независимые, т.е. алиас john и JoHn - один и тот же алиас с точки зрения хранилища ключей.

При создании ключа задается также следующая информация:

What is your first and last name?
What is the name of your organizational unit?
What is the name of your organization?
What is the name of your City or Locality?
What is the name of your State or Province?
What is the two-letter country code for this unit?


Здесь вводится информация о том, кто ты такой и где находишься.

Настройка Tomcat для использования ключа



Перемещаем файлик с ключом в каталог $CATALINA_HOME/webapps. После этого редактируем файлик $CATALINA_HOME/conf/server.xml. В этом файле раскомментируем коннектор на 8443 порту, добавим ссылку на файл с ключами и пароль для хранилища, получим:

<Connector port=”8443″
maxThreads=”150″ minSpareThreads=”25″ maxSpareThreads=”75″
enableLookups=”true” disableUploadTimeout=”true”
acceptCount=”100″ debug=”0″ scheme=”https” secure=”true”
clientAuth=”false” sslProtocol=”TLS”
keystoreFile=”/webapps/jdevnotes.keystore.bin”
keystorePass=”jdevpasswd” />


Теперь зайдите по адресу

https://localhost:8443

мы зашли на менеджер Томката по HTTPS. Теперь, любое приложение, которое развернуто на Томкате, может быть доступно как по HTTP, так и по HTTPS. Теперь рассмотрим, как сделать так, чтобы некоторые урлы были доступны только через HTTPS.

Настройка веб-приложения на работу только по HTTPS



Откроем файлик web.xml нашего приложения и добавим туда следующий код (код нужно помещать в самом конце контейнера web-app):

<security-constraint>
<web-resource-collection>
<web-resource-name>securedapp</web-resource-name>
<url-pattern>/billing/fullBalanceHistory</url-pattern>
<url-pattern>/mail/inbox</url-pattern>
<url-pattern>/somethingelse/*</url-pattern>
</web-resource-collection>
 
<user-data-constraint>
<transport-guarantee>CONFIDENTIAL</transport-guarantee>
</user-data-constraint>
</security-constraint>


Следующие ссылки будут доступны только по HTTPS:
/billing/fullBalanceHistory
/mail/inbox
/somethingelse/*

Если мы хотим, чтобы все приложение было доступно по HTTPS, то просто ставим паттерн /*. Если хотим вдруг выключить SSL, то для этого можно просто записать NONE в тег
<transport-guarantee>

вторник, 15 июня 2010 г.

Связка nginx - Tomcat: прокидывание реального IP пользователя до Томката

При разработке Evaphone мы решили все томкаты проксировать с помощью nginx. Это хорошо известная схема - frontend server - backend server. Frontend сервер (в нашем случае - nginx) занимается отдачей статики, а на backend он проксирует запросы на динамику. Такая схема работы позволяет снизить нагрузку на "тяжелый" бэкенд и увеличить производительность приложения.

Внутри нашего приложения используется информация о пользователи, а именно, IP-адрес. Когда запрос проксируется nginx'ом, то, естественно, реальный IP-адрес пользователя подменяется адресом машины, на которой работает nginx. Однако, nginx не "теряет" реальный IP-адрес. Вместо этого он передает его в HTTP-заголовке "x-real-ip". Это настраивается следующим конфигом nginx:


location = /tomcat {
proxy_pass ___tomcat_app_url___;
proxy_redirect off;

proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}


В этот конфиг вместо ___tomcat_app_url___ нужно подставить URL приложения на томкате, куда будет передаваться запрос. Например, может быть следующий URL: http://127.0.0.1:8080/myapp (это если tomcat и nginx работают на одной машине). Этот конфиг говорит томкату, что все запросы по адресу <ваш_ip>/tomcat нужно проксировать на ___tomcat_app_url___.

Теперь рассмотрим настройку Tomcat. Мы знаем, что реальный айпишник приходит к нам в заголовке запроса "x-real-ip". Подмену адреса (вернее, вытягивание реального айпишника) мы делаем с помощью сервлетного фильтра:

import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.FilterConfig;
import javax.servlet.FilterChain;
import javax.servlet.ServletResponse;
import javax.servlet.ServletRequest;
import javax.servlet.Filter;
 
public class GetRealIPFilter implements Filter {
 
public static final String X_FORWARDED_FOR = "x-forwarded-for";
public static final String X_REAL_IP = "x-real-ip";
 
public GetRealIPFilter() {
//System.out.println("[GetRealIPFilter.GetRealIPFilter]");
}
 
public void destroy() {
//System.out.println("[GetRealIPFilter.destroy]");
}
 
public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain)
throws ServletException, IOException
{
//System.out.println("[GetRealIPFilter.doFilter]");
if (request instanceof HttpServletRequest) {
 
HttpServletRequest httpRequest = (HttpServletRequest)request;
 
final String realIp = httpRequest.getHeader(X_REAL_IP);
final String realHost = httpRequest.getHeader(X_FORWARDED_FOR);
 
//System.out.println("[GetRealIPFilter.doFilter], realIp='"+realIp+"'");
//System.out.println("[GetRealIPFilter.doFilter], realHost='"+realHost+"'");
 
if (realIp != null) {
//System.out.println("[GetRealIPFilter.doFilter], realIp != null, wrap request");
 
filterChain.doFilter(
new HttpServletRequestWrapper(httpRequest) {
public String getRemoteAddr() {
return realIp;
}
public String getRemoteHost() {
return realHost;
}
},
response
);
return;
}
}
filterChain.doFilter(request, response);
}
 
public void init(FilterConfig filterConfig) throws ServletException {
//System.out.println("[GetRealIPFilter.init]");
}
}


Код достаточно понятный. Что мы делаем:

  1. Проверяем, есть ли заголовок "x-real-ip". Если его нет, то ничего не делаем, т.е. все остается по-старому. Tomcat продолжит принимать запросы напрямую

  2. Если заголовок "x-real-ip" присутствует, то в цепочку фильтров мы передаем не пришедший к нам от nginx request, а его обертку HttpServletRequestWrapper, в которой мы подменили методы getRemoteAddr и getRemoteHost так, чтобы они выдавали данные, пришедшие в заголовках "x-real-ip", "x-forwarded-for" соответственно.


Осталось только замэппить наш фильтр на все приложение. В файле web.xml добавляем следующие строки:

<filter>
<filter-name>get_real_ip_filter</filter-name>
<filter-class>GetRealIPFilter</filter-class>
</filter>
 
<filter-mapping>
<filter-name>get_real_ip_filter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>


На этом все!

Пара ссылок:

Настройка nginx для работы с apache и tomcat серверами на примере Ubuntu 10.04 Server

Nginx + Tomcat - тред на форуме nginx

JSP+Tomcat+nginx - тред на форуме RSDN

UPD 11 Июля 2010: Улучшенный код:

public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) 
throws ServletException, IOException
{
//System.out.println("[GetRealIPFilter.doFilter]");
if (request instanceof HttpServletRequest) {
 
HttpServletRequest httpRequest = (HttpServletRequest)request;
 
final String realIp = httpRequest.getHeader(X_REAL_IP);
final String realHost = httpRequest.getHeader(X_FORWARDED_FOR);
 
//System.out.println("[GetRealIPFilter.doFilter], realIp='"+realIp+"'");
//System.out.println("[GetRealIPFilter.doFilter], realHost='"+realHost+"'");
 
if (realIp != null) {
if (realHost != null) {
filterChain.doFilter(new HttpServletRequestWrapper(httpRequest) {
public String getRemoteAddr() {return realIp;}
public String getRemoteHost() {return realHost;}
},response);
return;
} else {
filterChain.doFilter(new HttpServletRequestWrapper(httpRequest) {
public String getRemoteAddr() {return realIp;}
},response);
return;
}
} else if (realHost != null) {
filterChain.doFilter(new HttpServletRequestWrapper(httpRequest) {
public String getRemoteHost() {return realHost;}
},response);
return;
}
}
filterChain.doFilter(request, response);
}

Постоянные читатели