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

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

вторник, 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);
}

Комментариев нет:

Отправить комментарий

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