2011-08-12 84 views
4

我試圖在Tomcat下啓用SSO,因此,訪問http://mydomain.comhttp://www.mydomain.com的用戶將可以使用針對http://subdomain.mydomain.com的請求提供會話Cookie。所有這三個域名都轉到同一個webapp,所以理想情況下我不想搞砸SSO,只需在標準的JSESSIONID cookie上設置域名即可。Tomcat - 沒有領域的SSO?

但是,這似乎不可能,所以我試圖啓用Tomcat的SSO閥。問題在於Valve需要定義一個Realm,而Realm應該指定一個用戶和角色的數據庫。但是,我沒有使用基於容器的身份驗證,也沒有使用基於角色的授權,所以我不需要或不想配置Realm。我只想讓會話cookie可以在這些不同的子域中共享。

有沒有簡單的方法來做到這一點?

編輯

我給這家目前的解決辦法是讓服務器重定向每個傳入的請求到「規範」的服務器名稱。這工作得很好,但顯然它並沒有真正解決問題。

回答

2

我們遇到同樣的問題,並創建了一個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) - 重置餅乾

在上下文配置您可以將閥門這樣

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

警告:在代碼中的Valve創建了幫助,如果沒有會議之前創建的,如果你需要一個會話都不在乎會話...

希望...祝你好運!

+0

謝謝,我會試試看。是否需要在'Context'上定義'Valve',還是可以將它作爲'Host'配置的一部分來完成? – aroth

+0

只要試一試,應該很容易 –