这里所说的跨域,是指跨二级域名,而且这些域名对应的应用都在同一个app上, 比如我有以下3个域名: 
www.vinceruan.info  
blog.vinceruan.info 
bbs.vinceruan.info 
我要在这三个域名直接共享cookie或者共享session,如何实现呢?在tomcat下又如何实现呢?

首先我们来了解下cookie,顾名思义,小甜心,少食即可,多吃无益。cookie是通过浏览器保存在客户端的临时数据,一般这些数据对安全的要求不高,虽然可以通过加密存放和SSL方式提交的方式加强cookie的安全,但不代表cookie就是百分之百安全的。再者,写入太多的cookie会造成数据管理的不可控,所以建议尽量少写cookie,像网易、新浪这些大型互联网公司,如果观察一下他们的站点,你会发觉他们会在浏览器写下大量的cookie,不过他们内部一般会有一套严格的cookie写入和清理的管理规定。比如网易邮件事业部就有这些规定。

cookie有两个很重要的属性:domain和path 
domain告诉浏览器当前要添加的cookie的域名归属,如果没有明确指明则默认为当前域名,比如通过访问www.vinceruan.info添加的cookie的域名默认就是www.vinceruan.info,通过访问blog.vinceruan.info所生成的cookie的域名就是blog.vinceruan.info. 
path告诉浏览器当前要添加的cookie的路径归属,如果没有明确指明则默认为当前路径,比如通过访问www.vinceruan.info/java/hotspot.html添加的cookie的默认路径就是/java/,通过blog.vinceruan.info/java/hotspot.html生成的cookie的路径也是/java/.

在清楚domain和path的生成规则后,我们需要知道浏览器在什么时候提交什么cookie到服务器,即浏览器是通过怎样的规则筛选cookie并提交到服务器的? 
浏览器提交的cookie需要满足以下两点: 
1.当前域名或者父域名下的cookie 
而且 
2.当前路径或父路径下的cookie 
要满足以上两个条件的cookie才会被提交.举个例子:有4个cookie: 
cookie1:[name=value, domain=.vinceruan.info path=/] 
cookie2:[name=value, domain=blog.vinceruan.info path=/java/] 
cookie3:[name=value, domain=www.vinceruan.info path=/] 
cookie4:[name=value, domain=blog.vinceruan.info path=/] 
当我访问blog.vinceruan.info时, 
cookie1可以被提交,因为.vinceruan.info是blog.vinceruan.info的父域名. path路径也一致. 
cookie2不能被提交,因为虽然domain是保持一致的,但是path不一致,当前访问的是/, 但是cookie2的path是/java/ 
cookie3不能被提交,因为虽然path是一致的,但是  www.vinceruan.info不是blog.vinceruan.info 的父域名. 
cookie4可以被提交,因为domain和cookie都严格保持一致.

这里需要注意的是, 在浏览器看来.  www.vinceruan.info不是blog.vinceruan.info 的父域名,而vinceruan.info才是blog.vinceruan.info的父域名,  www.vinceruan.info 也算是一个二级域名(这点如果你提交过域名到DNS服务器商的应该会知道,一般我们需要显式提交  www.vinceruan.info和vinceruan.info , 否则 www.vinceruan.info ==vinceruan.info是不成立的). 
所以如果我们需要在所有二级域名下共享islogin=1的cookie,用java代码如下: 
Cookie c = new Cookie("islogin","1"); 
c.setDomain("  .vinceruan.info ");//注意是以点号开头的. 
c.setPath=("/"); 
response.addCookie(c);

如果要在所有的二级域名下的/java/路径下共享silogin=1的cookie,用java代码如下: 
Cookie c = new Cookie("islogin","1"); 
c.setDomain("  .vinceruan.info ");//注意是以点号开头的. 
c.setPath=("/java/"); 
response.addCookie(c);

ok, 跨域共享cookie就完成了,比较容易,但是跨域共享session呢? 
首先我们需要了解session机制的原理, session都是保存在服务器端的,而http连接是无状态的,那么服务器是如何在多次独立的http连接中为你维持同一个session呢? 
这依赖于一个叫JSESSIONID的参数,这个参数可以从cookie或url获得,你如果你打开firebug,同时你的firefox是没有禁用cookie的,那么你刷新几次页面,你会看到浏览器提交的cookie中有一个叫JSESSIONID的key/value,这个就是会话ID,在这里你可以做一个试验,我们都知道firefox和IE 的cookie是不共享的,因此你在firefox下登录了,你用IE打开同一个网址,页面会显示你还处在未登录的状态,但是如果我在firefox下已经登录,并且拿到JSESSIONID的值,再到IE中输入url,并且加入JSESSION=.....的参数,你会发现,IE下也会显示我已经登录了。 
这说明,如果我通过网络截除到了别人的JESSIONID,我就可以在服务器的会话还没过期前访问该站,如果对方还是处于登录状态的,我就可以修改对方资料甚至密码,多么可怕,当然这一切都是理论上的。 
好了,一切都显得很顺利,要在不同的二级域名共享session,只需要把JESSIONID写进共享Cookie就行了。

可是,真的有这么容易么? 
不同语言的实现手法不一样,听PHP方面的高手说是很容易实现的,可是java就比较难了,首先我们知道了request.getSession(true)的原理。 
我这里tomcat为例,因为Java servlet api只是一套servlet接口规范,不同的java web容器实现的具体细节可能不同。 
在tomcat中,当我们调用request.getSession(true)时,如果实际上新建了一个session,那么就会往浏览器写入一个JSEESIONID的cookie.这个cookie的domain属性是跟你当前站的域名是严格保持一致的,即类似于www.vinceruan.info或 blog.vinceruan.info而不是.vinceruan.info, 所以如果我们在session创建时就写入一个共享的cookie就行了.

可是这还是有困难: 
1.如果我们在访问www.vinceruan.info新建了一个session的同时, 我写入一个JSESSIONID=****的、domain是.vinceruan.info的cookie, 理论上blog.vinceruan.info 也可以访问到在www.vinceruan.info这边创建的session了,但是很遗憾,如果我在访问www.vinceruan.info前先访问了blog.vinceruan.info, 同时创建了属于blog.vinceruan.info的session,这是再去访问www.vinceruan.info站点,同时建立了另一个session,并且建立了共享的JSESSIONID,这时再回去访问blog.vinceruan.info时,你会看到浏览器提交了两个JSESSIONID的cookie,类似于: 
JSESSIONID=EXAD2AD5FA4D1A5D4 path =/ domain=blog.vinceruan.info 
JSESSIONID=254SD2S5S1D2S5DS5 path = / domain=.vinceruan.info 
可以看出,后者才是我手工创建的,而前者是tomcat创建的。很遗憾,我手工创建的不生效,因为tomcat在绑定session时,采取严格匹配更加优先的原则,blog.vinceruan.info比.vinceruan.info更加匹配。 
所以问题已经转化为无论是那个应用,我要想办法在第一个创建session的应用中同时写入共享的JSESSIONID,即是说,我在写JSESSIONID到某个域下时,不允许这个域及其子域存在JSESSIONID不同值的情况。怎么解决呢?或许可以通过以下代码解决: 
public Session getSession(HttpServletRequest request, HttpServletResponse response){ 
      HttpSession session = request.getSession(false); 
       if (session==null){ 
             session = request.getSession(true); 
             String session_id = session.getId(); 
             Cookie c = new Cookie("JSESSIONID",session_id); 
             c.setDomain(".vinceruan.info"); 
             c.setPath("/"); 
             response.addCookie(); 
        } 

然后在所有应用中需要用到session的地方都调用这个方法,好像真的可以解决了,但是真的可以么? 
还是有问题: 
1.如果我已经往response里面输出body内容了,而这时调用这个方法请求session,且session是第一次被创建时会有问题。 
2.对于我调用的第三方代码,比如spring,它们里面调用session的方式不是我能控制的。

好的,这个方法已经明显不行了。 
还有一个HttpSessionListener,  它里面还有一个sessionCreated(SessionEvent event)的回调方法,无论是在什么代码中新建session,这个回调接口是一定会被调用的,但是,我拿不到HttpServletResponse句柄.所以这个方法还是不行.

我没有用SSO,是因为我这个应用的需求很简单,就是共享登录状态,而且就一个应用,只不过对应到不同的域名而已,所以没有必要用那么重量级的东西。

最后的修改是为tomcat增加一个Valve,同时注册到server.xml中去,这种方式也不太好,因为跟tomcat耦合了,但是也是目前可以想到的比较好的一个方法了。 
首先新建一个类: 
public class SessionCrossDomainValve extends ValveBase {

public SessionCrossDomainValve() { 
super(); 
info = "com.jinfuzi.SessionCrossDomainValve"; 
}

@Override 
public void invoke(Request request, Response response) throws IOException, ServletException { 
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; 
containerLog.debug("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); 
}

@SuppressWarnings("unchecked")   
      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   
          if (response.isCommitted())   
              containerLog.error("CrossSubdomainSessionValve: response was already committed!");   
     
          //find the Set-Cookie header for the existing cookie and replace its value with new cookie   
          MimeHeaders 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());  //如果是tomcat6.020,这里需要多加一个true. 
                      containerLog.debug("CrossSubdomainSessionValve: old Set-Cookie value: " + value.toString());   
                      containerLog.debug("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() { 
return ("CrossSubdomainSessionValve[container=" + container.getName() + ']'); 
}   

将这个类打包成jar包,放进{catalina_home}/lib下,并在server.xml中注册: 
<Valve className="SessionCrossDomainValve"/>

session跨域共享相关推荐

  1. 170222、使用Spring Session和Redis解决分布式Session跨域共享问题

    使用Spring Session和Redis解决分布式Session跨域共享问题 原创 2017-02-27 徐刘根 Java后端技术 前言 对于分布式使用Nginx+Tomcat实现负载均衡,最常用 ...

  2. 使用Spring Session和Redis解决分布式Session跨域共享问题

    大家可以关注一下公众号"Java架构师秘籍" 前言 对于分布式使用Nginx+Tomcat实现负载均衡,最常用的均衡算法有IP_Hash.轮训.根据权重.随机等.不管对于哪一种负载 ...

  3. session跨域共享解决方案

    session跨域共享解决方案 参考文章: (1)session跨域共享解决方案 (2)https://www.cnblogs.com/sustudy/p/5332584.html 备忘一下.

  4. Spring Boot 2.X + Shiro 优雅解决 session 跨域问题

    一.跨域问题 web 开发中跨域问题是一个老生常谈的问题,根本原因是浏览器基于安全原因考虑对非同源的脚本操作和 ajax 访问进行了限制,介绍的文章网上有很多,这里不做赘述. 二.解决方案 跨域问题有 ...

  5. Session跨域及单点登录解决方案

    cookie机制 关于cookie和seesion的联系 cookie中会包含那些信息 名字,值,过期时间,路径,域 cookie会带到http请求头中发送给服务端 如果cookie没有设置过期时间的 ...

  6. PHP解决http和https跨域,php中http与https跨域共享session的解决方法

    这篇文章主要介绍了http与https跨域共享session的解决方法,需要的朋友可以参考下 遇到了HTTP.HTTPS协议下session共享解决cookie失效的问题,这里提供一个临时解决办法. ...

  7. php跨域session共享,PHP中利用COOKIE与SESSION联合实现SESSION跨域

    大家都知道 在一般情况下,一个网站只有一个域名,www.demo.com,但是也有些网站架构是由多个子域名组建的.所以就需要SESSION可以跨子域被 访问到,这样才可以实现用户的跨域登录.就是说客户 ...

  8. 谈谈我的session跨域处理方法

    情景:公司的一个网站有一个模块(测试模块)需要单独用另外的一个域名(www.xyz.com)去访问,即网站需要用两个不同的域名去访问,如首页(www.abc.com)和测试模块(www.xyz.com ...

  9. think php框架 跨域,ThinkPHP框架实现session跨域问题

    这篇文章主要介绍了ThinkPHP框架实现session跨域问题的解决方法,需要的朋友可以参考下 ThinkPHP的session跨域问题很多开发者都遇到过! 其实不管是ThinkPHP还是php本身 ...

  10. Chrome 浏览器 Cookie 跨域共享与升级问题

    Chrome 51 版本开始,浏览器的 Cookie 新增加了一个SameSite属性,用来防止 CSRF 攻击和用户追踪.SameSite 可以设置三个值(Strict.Lax.None). 详见阮 ...

最新文章

  1. ajax交互扩展遇到的问题,ajax autocomplete扩展程序无法正常工作
  2. 零基础Java学习之初始化块
  3. python中统计计数的几种方法和Counter的介绍
  4. 笔记-信息化与系统集成技术-国务院关于印发新一代人工智能发展规划的通知...
  5. python趣味编程_第6章 旋转的方块(《Python趣味创意编程》教学视频)
  6. MOS2010的界面介绍和定制方法简介【资料汇集】
  7. php websocket应用实例,php使用websocket示例详解
  8. .net core orm框架_轻量级高性能PHP框架ycroute
  9. 如何测试软件的性能瓶颈,性能测试如何定位瓶颈
  10. caffe安装_【开源框架】caffe图像分类从模型自定义到测试
  11. html div画圆有什么用,圆形div栏,用于评级html5 js(Circle div bar for rating html5 js)
  12. vcpkg快速入门手册
  13. 毛星云opencv入门边缘检测Canny代码
  14. 电脑自动安装软件、各种弹窗广告、中病毒等问题解决方案
  15. [PPPOE]RP-PPPOE脚本分析
  16. python绘制函数曲线
  17. NCRE | 三级网络技术笔记
  18. 餐饮app开发市场前景如何?行业竞争激烈吗?
  19. 关于注册Google账号时遇到“此电话号码无法用于进行验证”的问题的解决方法
  20. 刷程序对车危害_刷ECU是什么意思,对车子的伤害有多大?

热门文章

  1. 网站域名假墙处理方法 内含cloudflare API自动更换IP的php脚本
  2. 织梦dedecms 模板代码标签学习
  3. WinExec、ShellExecute、CreateProcess iexplore.exe打开网页
  4. 智慧养老整体解决方案
  5. 关于信号发生器的功能和参数介绍(二)
  6. JVM常见面试题汇总笔记
  7. 【干货】啦啦啦~再也不用担心webStorm激活码失效了
  8. 无限循环滚动代码阿里巴巴国际站店铺装修代码底图滚动黑色半透明显示效果自定义内容装修代码全屏显示
  9. 华南理工大学计算机学院创新班,高校纷推创新班揽生源 逐步淘汰冷门专业
  10. None of the following candidates is applicable because of a receiver type mismatch