zoukankan      html  css  js  c++  java
  • ajax 跨域 session 及 spring boot分布式session

    这里写图片描述

    未跨域时Ajax请求不同的action时,session保持不变

    这里写图片描述

    跨域Ajax异步请求时,每次请求都是一个新的session

    这里写图片描述

    一些帖子供参考

    http://www.cnblogs.com/interdrp/p/4056525.html 
    http://blog.csdn.net/qq_29845761/article/details/51897705

    同一个容器内的请求不会出现session失效,异步跨域访问则需要考虑session共享问题



    (二)参考http://www.cnblogs.com/gdlin/p/6846638.html

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

    这时候就涉及到session跨域问题,因为域名不是父子关系,所以必须要实现完全跨域,想到了以下三个解决办法:

    1.URL传参:测试模块访问的时候,地址www.xyz.com后把主域名的session通过参数的形式传递过去,如:www.xyz.com;jsessionid=7D4DED1F2DB5BC53961EFED18BCE7E30

    2.SSO单点登录

    3.利用jsonp的跨域特性,通过ajax进行session传递

    第一种方案考虑URL传参数不美观以及URL需要分享给别人的时候就没法获取到session,所以放弃

    第二种SSO单点登录方案应该在三种方案中最优的,因为系统架构问题,所以也放弃

    我的项目中采用第1种方案,直接成功。

    在本例中,对方的前提是 服务器相同,域名不同,且非父子域名cookie无法共享


    (三)周边情况

    http://blog.csdn.net/ahhsxy/article/details/7356128

    这篇文章前提是   服务器相同,域名不同,父子域名,文章详细分析了sesseion本质及解决方案

    文章首先:要在不同的二级域名共享session,只需要把JESSIONID写进共享Cookie就行了。

    然后发现session后者才是我手工创建的,而前者是tomcat创建的。很遗憾,我手工创建的不生效,因为tomcat在绑定session时,采取严格匹配更加优先的原则,blog.vinceruan.info比.vinceruan.info更加匹配。

    所以:在所有sesseion的地方:

    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,重置cokkie的domain为父域名,从根源上消除二级域名的cokkie,返回到客户端的cokkie只有一级域名的

    本人认为,此法开销挺大,先要找到所有session的地方,如果有落网之鱼,出现了一个二级域名的cokkie,tomcat会以最佳匹配方式选中二级域名的session,多域名session即刻失效,比如:

    访问B.xx.com,取得xx.com的cokkie——访问A.xx.com的函数a(此函数需要验证身份),以一级域名xx.com验证身份成功——访问A.qq.com的函数b(此函数b未处理session的域名,且无需验证身份),虽然验证身份失败,但是名义上请求也成功了-因为b未对身份做权限拦截,且额外生成一个A.xx.com的cokkie,污染了整个跨域名session系统——再回来访问访问A.xx.com的函数a,由于本地cokkie存在两个,浏览器会将2个都放在request_header中发送请求,tomcat以最佳匹配原则取了A.xx.com的cokkie,此cokkie是tomcat在访问函数b时自动生成的session,里面无相关权限信息,至此客户端在不知不觉中验证身份失败了,虽然两次访问同一个请求函数a,结果却莫名不同

    后话:当然也可以在HttpSessionListener中统一处理


    作者也表示不想使用单点登录,所以最终采用修改tomcat配置搞定





    (四)基于redis的单点登录

    http://blog.csdn.net/Angry_Mills/article/details/73866332

    先简单说一下单点登录

    把登录的部分单独拿出来作为一个项目,专门用来登录。

    当我想访问某个子项目或者模块的时候,会先请求登录的部分,如果登录过了,就不需要再登录了。

    这个和单独项目时,把userId放到session中道理是一样的

    因为多个子项目在不同的tomcat无法实现session共享,这时我们可以利用cookie


    多说几句关于cookie和session的区别

    cookie是针对于浏览器的,session是针对服务器的。

    cookie的产生会有JSessionId,也就是session的唯一凭证。

    cookie是当前浏览器对该父域名用作数据共享和记录的。      

    session是在服务器上开辟了一块内存,用于当前session(同一JSessionId)用户的数据共享。

    关于cookie和session的区别只是个人理解,有不同的看法可以提,多交流。


    说一下流程:

    用户在一个模块登录,会进到登录系统,先验证你的用户名和密码是否正确,如果正确。

    会产生一个唯一票据,我们这里叫他token,把这个token放到redis中,存放方式是这样的。

    redis.put(token,userId);//存储一条对应用户信息的token

    redis,put("your setting",token);//再存储一条对应userId的唯一标识,your setting可以写成loginSys+userId

    为什么要这么存,正常的思路是只存上面一条就行了,稍后解答。


    我们把token返回,response.addCookie(new Cookie("my cookie",token));//这里的my cookie可以写成loingSysCookie

    流程实现是这样的,需要设置过期时间等等参数,这里就不写了。


    这时候只要是同一浏览器,就会带着这个cookie,访问该项目下的子项目(父级域名下的子级域名,比如www.baidu.com和mp3.baidu.com的关系),

    进入登录系统,拿到token,进到redis中比对,是否有用户就可以了,如果有,就不需要再登录了。

    下面说一下上面的疑问:

    当我再一次登录系统的时候,先去找redis中的redis,get("your setting")是否为空,已经在别处登录过的用户,可以取到他的token,

    redis.del(token),这样就实现了单点登录,同步登出。

    之后再设置redis和cookie,方法同上。


    很多没有redis中放第二条,就无法实现单点登录同步登出,虽然设置了过期时间,但用户体验和安全性还是差一点。



    该法没有用session,仅用了cokkie,所以不存在tomcat的cokkie和自己生成的session cookie冲突问题


    但仍有两个弊端:

    1.如果不是父子域名就没办法了

    2. 客户端禁用cookie


    总结:

    1.如果请求域名不同,存在父子关系

    只能放弃session+cookie方式,因为一次登录要给父域名注册cookie,且还要考虑tomcat自动生成的二级域名cookie与手动注册的一级域名cookie冲突,每次覆盖sesseion(无谓的开销)以及spring等框架对session的封装;

    可采用cokkie(或localstrage)+token(jsessionID),在请求时一起带上


    2.如果请求域名不同,不存在父子关系

    可采用localstrage+token(jsessionID),在请求时一起带上


    3.ajax请求不会写入cokkie,也不会带上cokkie,所以只能在请求时捎带 jsessionid 或者token机制


    4.如果请求域名相同,服务器不同

    则直接在服务器端集成分布式session;

    spring boot 分布式session(利用redis)方法参考:

    http://www.cnblogs.com/mengmeng89012/p/5519698.html


    这次带来的是spring boot + redis 实现session共享的教程。

     

    在spring boot的文档中,告诉我们添加@EnableRedisHttpSession来开启spring session支持,配置如下:

    Java代码  
    1. @Configuration  
    2. @EnableRedisHttpSession  
    3. public class RedisSessionConfig {  
    4. }  

    而@EnableRedisHttpSession这个注解是由spring-session-data-redis提供的,所以在pom.xml文件中添加:

    Java代码  
    1. <dependency>  
    2.         <groupId>org.springframework.boot</groupId>  
    3.         <artifactId>spring-boot-starter-redis</artifactId>  
    4. </dependency>  
    5. <dependency>  
    6.         <groupId>org.springframework.session</groupId>  
    7.         <artifactId>spring-session-data-redis</artifactId>  
    8. </dependency>  

     

     

    接下来,则需要在application.properties中配置redis服务器的位置了,在这里,我们就用本机:

    Java代码  
    1. spring.redis.host=localhost  
    2. spring.redis.port=6379  

    这样以来,最简单的spring boot + redis实现session共享就完成了,下面进行下测试。

     

    首先我们开启两个tomcat服务,端口分别为8080和9090,在application.properties中进行设置【下载地址】   

    Java代码  
    1. server.port=8080  

     

    接下来定义一个Controller: 

    Java代码  
    1. @RestController  
    2. @RequestMapping(value = "/admin/v1")  
    3. public class QuickRun {  
    4.     @RequestMapping(value = "/first", method = RequestMethod.GET)  
    5.     public Map<String, Object> firstResp (HttpServletRequest request){  
    6.         Map<String, Object> map = new HashMap<>();  
    7.         request.getSession().setAttribute("request Url", request.getRequestURL());  
    8.         map.put("request Url", request.getRequestURL());  
    9.         return map;  
    10.     }  
    11.   
    12.     @RequestMapping(value = "/sessions", method = RequestMethod.GET)  
    13.     public Object sessions (HttpServletRequest request){  
    14.         Map<String, Object> map = new HashMap<>();  
    15.         map.put("sessionId", request.getSession().getId());  
    16.         map.put("message", request.getSession().getAttribute("map"));  
    17.         return map;  
    18.     }  
    19. }  

     

    启动之后进行访问测试,首先访问8080端口的tomcat,返回 获取【下载地址】   

    Java代码  
    1. {"request Url":"http://localhost:8080/admin/v1/first"}  

     接着,我们访问8080端口的sessions,返回:

    Java代码  
    1. {"sessionId":"efcc85c0-9ad2-49a6-a38f-9004403776b5","message":"http://localhost:8080/admin/v1/first"}  

    最后,再访问9090端口的sessions,返回:

    Java代码  
    1. {"sessionId":"efcc85c0-9ad2-49a6-a38f-9004403776b5","message":"http://localhost:8080/admin/v1/first"}  

    可见,8080与9090两个服务器返回结果一样,实现了session的共享

     

    如果此时再访问9090端口的first的话,首先返回:

    Java代码  
    1. {"request Url":"http://localhost:9090/admin/v1/first"}  

    而两个服务器的sessions都是返回:

    Java代码  
    1. {"sessionId":"efcc85c0-9ad2-49a6-a38f-9004403776b5","message":"http://localhost:9090/admin/v1/first"}  

     

    通过spring boot + redis来实现session的共享非常简单,而且用处也极大,配合nginx进行负载均衡,便能实现分布式的应用了。

    本次的redis并没有进行主从、读写分离等等配置(_(:з」∠)_其实是博主懒,还没尝试过.......)

    而且,nginx的单点故障也是我们应用的障碍......以后可能会有对此次博客的改进版本,比如使用zookeeper进行负载均衡,敬请期待。


  • 相关阅读:
    Cookie
    精英讲师培训笔记03-如何与台下观众有效互动
    精英讲师培训笔记02-培训师手势如何做
    精英讲师培训笔记01-提升口才的三个心法
    "怒海争锋"沙盘培训思考
    logback问题集
    spring boot2 启动过程
    Connect reset
    ELK 安装及使用
    常用中文教程网站
  • 原文地址:https://www.cnblogs.com/silyvin/p/9106766.html
Copyright © 2011-2022 走看看