最近启用二级域名后,面临一个主域名与二级域名之间 session 不能共享的问题,带来的麻烦就是用户在主域名登陆,但由于二级域名 session 不能共享 ,因此无法进行登陆的操作,对一些功能有一些影响。
问题的原因如下:
Tomcat 下,不同的二级域名,Session 默认是不共享的,因为 Cookie 名称为 JSESSIONID 的 Cookie
根域是默认是没设置的,访问不同的二级域名,其 Cookie 就重新生成,而 session 就是根据这个 Cookie
来生成的,所以在不同的二级域名下生成的 Session 也不一样。
找到了其原因,就可根据这个原因对 Tomcat 在生成 Session 时进行相应的修改。
快速解决方案1:
在项目的/MET-INF/ 目录下创建一个 context.xml 文件,内容为:
1
2
| <?xml version="1.0" encoding="UTF-8"?>
<Context useHttpOnly="true" sessionCookiePath="/" sessionCookieDomain=".XXXX.com" />
|
Done!
快速解决方案2:修改 Tomcat 的 server.xml 文件,内容为:
1
| <Context path="" docBase="ROOT" reloadable="false" useHttpOnly="true" sessionCookiePath="/" sessionCookieDomain=".XXXX.com" />
|
Done!
以上两种方案的详细讲解见:http://tomcat.apache.org/tomcat-6.0-doc/config/context.html
快速解决方案3:生成一个叫做 crossSubdomainSessionValve.jar 的文件,用的时候放在
Tomcat lib 目录下,然后修改 Tomcat server.xml 文件:
1
| <Valve className="me.seanchang.CrossSubdomainSessionValve" />
|
原理:取代由 Tomcat 域产生的会话 cookie ,允许该会话 cookie
跨子域共享。
代码:
| package me.seanchang;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
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.buf.MessageBytes;
import org.apache.tomcat.util.http.MimeHeaders;
import org.apache.tomcat.util.http.ServerCookie;
public class CrossSubdomainSessionValve extends ValveBase {
private static Logger log = Logger.getLogger("CrossSubdomainSessionValve");
public CrossSubdomainSessionValve() {
super();
info = "me.seanchang.CrossSubdomainSessionValve/1.0";
}
@Override
public void invoke(Request request, Response response) throws IOException,
ServletException {
// this will cause Request.doGetSession to create the session cookie if
// necessary
request.getSession(true);
// replace any Tomcat-generated session cookies with our own
Cookie[] cookies = response.getCookies();
if (cookies != null) {
for (int i = 0; i < cookies.length; i++) {
Cookie cookie = cookies[i];
log.info("CrossSubdomainSessionValve: Cookie name is "
+ cookie.getName());
if (Globals.SESSION_COOKIE_NAME.equals(cookie.getName()))
replaceCookie(request, response, cookie);
}
}
// process the next valve
getNext().invoke(request, response);
}
protected void replaceCookie(Request request, Response response, Cookie cookie) {
// copy the existing session cookie, but use a different domain
Cookie newCookie = new Cookie(cookie.getName(), cookie.getValue());
if (cookie.getPath() != null)
newCookie.setPath(cookie.getPath());
newCookie.setDomain(getCookieDomain(request));
newCookie.setMaxAge(cookie.getMaxAge());
newCookie.setVersion(cookie.getVersion());
if (cookie.getComment() != null)
newCookie.setComment(cookie.getComment());
newCookie.setSecure(cookie.getSecure());
// if the response has already been committed, our replacement strategy
// will have no effect
MimeHeaders headers = new MimeHeaders();
if (response.isCommitted())
log.info("CrossSubdomainSessionValve: response was already committed!");
// find the Set-Cookie header for the existing cookie and replace its
// value with new cookie
headers = response.getCoyoteResponse().getMimeHeaders();
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(cookie.getName()) >= 0) {
StringBuffer buffer = new StringBuffer();
ServerCookie
.appendCookieValue(buffer, newCookie.getVersion(),
newCookie.getName(), newCookie.getValue(),
newCookie.getPath(), newCookie.getDomain(),
newCookie.getComment(),
newCookie.getMaxAge(),
newCookie.getSecure(), true);
log.info("CrossSubdomainSessionValve: old Set-Cookie value: "
+ value.toString());
log.info("CrossSubdomainSessionValve: new Set-Cookie value: "
+ buffer);
value.setString(buffer.toString());
}
}
}
}
protected String getCookieDomain(Request request) {
String cookieDomain = request.getServerName();
String[] parts = cookieDomain.split("\\.");
if (parts.length >= 2)
cookieDomain = parts[parts.length - 2] + "."
+ parts[parts.length - 1];
return "." + cookieDomain;
}
public String toString() turn ("CrossSubdomainSessionValve[container=" + container.getName() + ']');
}
}
|
将以上代码导出一个jar文件,放入 $CATALINA_HOME/lib 中,修改 $CATALINA_HOME/conf/server.xml 文件,加入:
1
| <Valve className="me.seanchang.CrossSubdomainSessionValve" />
|
重启 Tomcat ,Done !