Tomcat - SSO без королевства? - PullRequest
       3

Tomcat - SSO без королевства?

4 голосов
/ 12 августа 2011

Я пытаюсь включить единый вход в Tomcat, чтобы у пользователей, которые переходят на http://mydomain.com и http://www.mydomain.com, были доступны файлы cookie сеанса для запросов на http://subdomain.mydomain.com. Все три эти домены идут в одно и то же веб-приложение, поэтому в идеале я бы вообще не хотел связываться с SSO, а просто установил домен в стандартный файл cookie JSESSIONID.

Однако это не представляется возможным, поэтому я пытаюсь включить SSO-клапан Tomcat. Проблема в том, что Valve требует определения области, а область должна указывать базу данных пользователей и ролей. Однако я не использую аутентификацию на основе контейнеров или авторизацию на основе ролей, поэтому мне не нужно или не нужно настраивать область. Все, что я хочу, - это чтобы файлы cookie сеанса могли совместно использоваться в каждом из этих различных поддоменов.

Есть ли прямой способ сделать это?

Редактировать

Мой текущий обходной путь для этого состоит в том, чтобы сервер перенаправлял каждый входящий запрос на «каноническое» имя сервера. Это работает достаточно хорошо, но, очевидно, на самом деле это не решает проблему.

1 Ответ

2 голосов
/ 15 августа 2011

У нас возникла та же проблема, и мы создали Tomcat Valve, который перезаписывал или устанавливал часть домена сеанса Cookie. Довольно простая вещь, и она работает уже много лет. Код выглядит так:

public class CrossSubdomainSessionValve extends ValveBase {
  public CrossSubdomainSessionValve() {
    super();
    info = "common-tomcat-CrossSubdomainSessionValve";
  }

  @Override
  public void invoke(Request request, Response response) throws IOException, ServletException {
    // cookie will only need to be changed, if this session is created by this request.
    if (request.getSession(true).isNew()) {
      Cookie sessionCookie = findSessionCookie(response.getCookies());
      if (sessionCookie != null) {
        String cookieDomainToSet = getCookieDomainToSet(request.getServerName());
        if (cookieDomainToSet != null) {
          // changing the cookie only does not help, because tomcat immediately sets
          // a string representation of this cookie as MimeHeader, thus we also
          // have to change this representation
          replaceCookie(response.getCoyoteResponse().getMimeHeaders(), sessionCookie, cookieDomainToSet);
        }
      }
    }

    // process the next valve
    getNext().invoke(request, response);
  }

  protected Cookie findSessionCookie(Cookie[] cookies) {
    if (cookies != null)
      for (Cookie cookie : cookies)
        if (Globals.SESSION_COOKIE_NAME.equals(cookie.getName())) {
          return cookie;
    return null;
  }

  protected void replaceCookie(MimeHeaders headers, Cookie originalCookie, String domainToSet) {
    // if the response has already been committed, our replacementstrategy will have no effect

    // find the Set-Cookie header for the existing cookie and replace its value with new cookie
    for (int i = 0, size = headers.size(); i < size; i++) {
      if (headers.getName(i).equals("Set-Cookie")) {
        MessageBytes value = headers.getValue(i);
        if (value.indexOf(originalCookie.getName()) >= 0) {
          if (originalCookie.getDomain() == null) {
            StringBuilder builder = new StringBuilder(value.getString()).append("; Domain=").append(domainToSet);
            value.setString(builder.toString());
          } else {
            String newDomain = value.getString().replaceAll("Domain=[A-Za-z0-9.-]*", "Domain=" + domainToSet);
            value.setString(newDomain);
          }
        }
      }
    }
  }

  protected String getCookieDomainToSet(String cookieDomain) {
    String[] parts = cookieDomain.split("\\.");
    if (parts.length >= 3) {
      return "." + parts[parts.length - 2] + "." + parts[parts.length - 1];
    }
    return null;
  }

  public String toString() {
    return ("CrossSubdomainSessionValve[container=" + container.getName() + ']');
  }
}

Алгоритм работает так: - Только если сеанс новый - найти cookie сеанса - Получить запрошенное имя хоста - Разделите имя хоста с помощью «.» - Если в нем хотя бы 3 части (например, www.google.de), удалите первую часть (на .google.de). - Сбросить cookie

В вашей конфигурации Context вы можете применить клапан следующим образом

<Valve className="my.package.CrossSubdomainSessionValve" httpOnlyEnabled="true" />

Предупреждение: в коде Valve создает сеанс, если до этого сеанс не создавался, и не заботится, нужен ли вам сеанс вообще ...

Надеюсь, это поможет ... Удачи!

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...