Tomcat - 没有领域的 SSO?

发布于 2024-11-29 17:04:11 字数 668 浏览 0 评论 0 原文

我正在尝试在 Tomcat 下启用 SSO,以便访问 http://mydomain.comhttp://www.mydomain.com 的会话 cookie 可用于向 http://subdomain.mydomain.com。所有这三个域都转到同一个 Web 应用程序,因此理想情况下,我不想弄乱 SSO,而只需在标准 JSESSIONID cookie 上设置域。

然而,这似乎不可能,所以我尝试启用 Tomcat 的 SSO Valve。问题是 Valve 需要一个 Realm 的定义,而 Realm 应该指定用户和角色的数据库。但是,我没有使用基于容器的身份验证,也没有使用基于角色的授权,因此我不需要或不想配置领域。我想要的只是会话 cookie 能够在每个不同的子域之间共享。

有什么简单的方法可以做到这一点吗?

编辑

我当前的解决方法是让服务器将每个传入请求重定向到“规范”服务器名称。这很有效,但显然它并没有真正解决问题。

I'm trying to enable SSO under Tomcat such that users who go to http://mydomain.com and http://www.mydomain.com will have their session cookie available for requests made to http://subdomain.mydomain.com. All three of these domains go to the same webapp, so ideally I'd like to not mess with SSO at all and just set the domain on the standard JSESSIONID cookie.

However, that doesn't seem possible, so I'm trying to enable Tomcat's SSO Valve. The problem is that the Valve requires a definition of a Realm, and a Realm is supposed to specify a database of users and roles. However, I am not using container-based authentication nor role-based authorization, so I do not need or want to configure a Realm. All I want is for the session cookie(s) to be able to be shared across each of these different subdomains.

Is there any straightforward way to do this?

Edit

My current workaround for this is to have the server redirect every incoming request to the "canonical" server name. This works well enough, but obviously it is not actually solving the problem.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

溺渁∝ 2024-12-06 17:04:11

我们遇到了同样的问题,并创建了一个 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

在您的上下文配置中,您可以像这样应用 Valve

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

警告:在代码中,如果之前没有创建会话,则 Valve 会创建一个会话,并且根本不关心您是否需要会话。 ..

希望有帮助...祝你好运!

We were having the same problem and created a Tomcat Valve that would overwrite or set the Domain part of the session Cookie. Quite a simple thing and it already works for many years. The code goes like this:

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() + ']');
  }
}

The algorithm works like this:
- Only if the session is new - find the session cookie
- Get the requested host name
- Split the host name with '.'
- If it has at least 3 parts (like www.google.de), remove first part (to .google.de)
- Reset the cookie

In your Context configuration you can apply the valve like this

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

Caveat: In the code the Valve creates a session if no session was created before and does not care if you need a session at all...

Hope that helps... Good luck!

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文