Описание задачи:
Вы делаете проект на java под Tomcat. И решили организовать разделы своего сайта в виде поддоменов.
К примеру, на сайте www.domen.xx сделать разделы: mail.domen.xx, user.domen.xx и т.п. В какой-то момент разработки вы с удивлением обанружите, что пользовательская сессия вопреки ожиданиям существует строго в рамках одного домена. То есть юзер авторизовавшись на главной странице (www.domen.xx), переходя почту (mail.domen.xx), теряет авторизацию.
Дело в том, что сессия привязывается к клиенту через cookie с именем JSESSIONID и с пустым доменом. А когда домен не указан, браузер использует полный текущий домен. То есть сессия привязывается не к «domen.xx», а к «www.domen.xx». По неизвестной мне причине в Томкэте нет настроек позволяющих управлять этим поведением.
Как лечится:
Сначала чуть подробней про сессии. Сессия получается через вызов метода request.getSession(). Внутри метода читается кука JSESSIONID и из «хранилища» достается соответствующая ей сессия. Если сессии нет, она создается и в заголовок ответа пишется кука с новым значением JSESSIONID. Вот здесь и появляемся мы! Все что нужно — это дописать в куку нужное значение домена (domen.xx).
Tomcat предоставляет механизм «клапанов» (Valve). Фактически valve — это java-класс, который вызывается перед обработкой каждого запроса. И как догадывается читатель — именно этот механизм и будет использоваться в реализации. Надо создать Valve-класс, который перед обработкой каждого запроса будет обращаться к сессии. И подменять куку, если сессия была только что создана.
Реализация:
Valve-класс:
package my.tomcat;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
import org.apache.catalina.Globals;
import org.apache.catalina.connector.Request;
import org.apache.catalina.connector.Response;
import org.apache.catalina.valves.ValveBase;
import org.apache.tomcat.util.http.ServerCookie;
public class SubdomainSessionValve extends ValveBase {
/** Общий домен для всех разделов */
private static final String DOMAIN = "domain.xx";
@Override
public String getInfo() {
return "my.tomcat.SubdomainSessionValve/1.0a";
}
@Override
public void invoke(Request req, Response res)
throws IOException, ServletException {
fixSessionIdCookie(req);
getNext().invoke(req, res);
}
private void fixSessionIdCookie(Request request) {
if(getJsessionCookie(request.getCookies()) != null)
return;
request.getSession();
Cookie cookie = getJsessionCookie(request.getResponse().getCookies());
if(cookie == null)
return;
cookie.setDomain(DOMAIN);
StringBuffer sb = new StringBuffer();
ServerCookie.appendCookieValue(sb,
cookie.getVersion(), cookie.getName(), cookie.getValue(),
cookie.getPath(), cookie.getDomain(), cookie.getComment(),
cookie.getMaxAge(), cookie.getSecure());
request.getResponse().setHeader("Set-Cookie", sb.toString());
}
Cookie getJsessionCookie(Cookie[] cc) {
for (int i = 0; cc != null && i < cc.length; i++) {
if(Globals.SESSION_COOKIE_NAME.equals(cc[i].getName()))
return cc[i];
}
return null;
}
}
* This source code was highlighted with Source Code Highlighter.
Осталось положить jar с этим классом в ${TOMCAT_HOME}/server/lib и прописать в server.xml:
<Valve className=«my.tomcat.SubdomainSessionValve»/>
Либо при запуске сервера из приложения:
org.apache.catalina.Context context;
....... // инициалиация и запуск сервера
context.getPipeline().addValve(new my.tomcat.SubdomainSessionValve());
* This source code was highlighted with Source Code Highlighter.
Спасибо за внимание.
Удачи!