我正在尝试在Tomcat下启用SSO,以便转到
http://mydomain.com和
http://www.mydomain.com的用户将会话cookie用于发送到
http://subdomain.mydomain.com的请求.所有这三个域都转到同一个webapp,所以理想情况下我不想弄乱完全使用SSO,只需在标准的JSESSIONID cookie上设置域即可.
但是,这似乎不可能,所以我正在尝试启用Tomcat的SSO Valve.问题是Valve需要定义Realm,而Realm应该指定用户和角色的数据库.但是,我不使用基于容器的身份验证,也不使用基于角色的授权,因此我不需要或不想配置Realm.我想要的只是会话cookie可以在这些不同的子域中共享.
有没有直接的方法来做到这一点?
编辑
我目前的解决方法是让服务器将每个传入请求重定向到“规范”服务器名称.这很好用,但显然它实际上并没有解决问题.
最佳答案 我们遇到了同样的问题并创建了一个Tomcat Valve,它会覆盖或设置会话Cookie的Domain部分.相当简单的事情,它已经工作了很多年.代码如下:
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会创建一个会话,并且根本不关心您是否需要会话…
希望有所帮助……祝你好运!