Pull to refresh

(Java) Tomcat: делаем кросс-доменную сессию

Reading time3 min
Views12K

Описание задачи:


Вы делаете проект на 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.


Спасибо за внимание.
Удачи!
Tags:
Hubs:
Total votes 30: ↑30 and ↓0+30
Comments36

Articles