从零开始的Spring Session(一)

本文做者:徐靖峰
原文连接:http://t.cn/Rp7xUro
版权归做者全部,转载请注明出处html

 

以前项目须要使用Redis管理Session,找了不少的Demo,研究了一天Spring Session源码,感受有点难继续。忽然看到群里Pivotal的周辉大牛发了一篇链接,忽然豁然开朗。html5

 

Session和Cookie这两个概念,在学习java web开发之初,大多数人就已经接触过了。最近在研究跨域单点登陆的实现时,发现对于Session和Cookie的了解,并非很深刻,因此打算写两篇文章记录一下本身的理解。在咱们的应用集成Spring Session以前,先补充一点Session和Cookie的关键知识。java

Session与Cookie基础

因为http协议是无状态的协议,为了可以记住请求的状态,因而引入了Session和Cookie的机制。咱们应该有一个很明确的概念,那就是Session是存在于服务器端的,在单体式应用中,他是由tomcat管理的,存在于tomcat的内存中,当咱们为了解决分布式场景中的session共享问题时,引入了redis,其共享内存,以及支持key自动过时的特性,很是契合session的特性,咱们在企业开发中最经常使用的也就是这种模式。可是只要你愿意,也能够选择存储在JDBC,Mongo中,这些,spring都提供了默认的实现,在大多数状况下,咱们只须要引入配置便可。而Cookie则是存在于客户端,更方便理解的说法,能够说存在于浏览器。Cookie并不经常使用,至少在我不长的web开发生涯中,并无什么场景须要我过多的关注Cookie。http协议容许从服务器返回Response时携带一些Cookie,而且同一个域下对Cookie的数量有所限制,以前说过Session的持久化依赖于服务端的策略,而Cookie的持久化则是依赖于本地文件。虽说Cookie并不经常使用,可是有一类特殊的Cookie倒是咱们须要额外关注的,那即是与Session相关的sessionId,他是真正维系客户端和服务端的桥梁。web

代码示例

用户发起请求,服务器响应请求,并作一些用户信息的处理,随后返回响应给用户;用户再次发起请求,携带sessionId,服务器便可以识别,这个用户就是以前请求的那个。redis

使用Springboot编写一个很是简单的服务端,来加深对其的理解。需求很简单,当浏览器访问localhost:8080/test/cookie?browser=xxx时,若是没有获取到session,则将request中的browser存入session;若是获取到session,便将session中的browser值输出。顺便将request中的全部cookie打印出来。spring

@Controller
public class CookieController {
    @RequestMapping("/test/cookie")
    public String cookie(@RequestParam("browser") String browser, HttpServletRequest request, HttpSession session) {
        //取出session中的browser
        Object sessionBrowser = session.getAttribute("browser");
        if (sessionBrowser == null) {
            System.out.println("不存在session,设置browser=" + browser);
            session.setAttribute("browser", browser);
        } else {
            System.out.println("存在session,browser=" + sessionBrowser.toString());
        }
        Cookie[] cookies = request.getCookies();
        if (cookies != null && cookies.length > 0) {
            for (Cookie cookie : cookies) {
                System.out.println(cookie.getName() + " : " + cookie.getValue());
            }
        }
        return "index";
    }
}

咱们没有引入其余任何依赖,看看原生的session机制是什么。chrome

1 使用chrome浏览器,访问localhost:8080/test/cookie?browser=chrome,控制台输出以下:后端

 

1api

 

Session Info: 不存在session,设置browser=chrome跨域

既没有session,也没有cookie,而且根据咱们将browser=chrome已经设置到了session中。

再次访问一样的地址,控制台输出以下:

 

1

2

 

Session Info: 存在session,browser=chrome

Cookie Info: JSESSIONID : 4CD1D96E04FC390EA6C60E8C40A636AF

屡次访问以后,控制台依旧打印出一样的信息。

稍微解读下这个现象,能够验证一些结论。当服务端往session中保存一些数据时,Response中自动添加了一个Cookie:JSESSIONID:xxxx,再后续的请求中,浏览器也是自动的带上了这个Cookie,服务端根据Cookie中的JSESSIONID取到了对应的session。这验证了一开始的说法,客户端服务端是经过JSESSIONID进行交互的,而且,添加和携带key为JSESSIONID的Cookie都是tomcat和浏览器自动帮助咱们完成的,这很关键。

2 使用360浏览器,访问localhost:8080/test/cookie?browser=360

第一次访问:

 

1

 

Session Info: 不存在session,设置browser=360

后续访问:

 

1

2

 

Session Info: 存在session,browser=360

Cookie Info: JSESSIONID : 320C21A645A160C4843D076204DA2F40

为何要再次使用另外一个浏览器访问呢?先卖个关子,咱们最起码能够得出结论,不一样浏览器,访问是隔离的,甚至从新打开同一个浏览器,JSESSIONID也是不一样的。

安全问题

其实上述的知识点,都是很是浅显的,之因此啰嗦一句,是为了引出这一节的内容,以及方便观察后续咱们引入Spring Session以后的发生的变化。

还记得上一节的代码示例中,咱们使用了两个浏览器:

  • chrome浏览器访问时,JSESSIONID为4CD1D96E04FC390EA6C60E8C40A636AF,后端session记录的值为:browser=chrome
  • 360浏览器访问时,JSESSIONID为320C21A645A160C4843D076204DA2F40,后端session记录的值为:browser=360。

咱们使用chrome插件Edit this Cookie,将chrome浏览器中的JSESSIONID修改成360浏览器中的值

EditThisCookieEditThisCookie

一样访问原来的端点:localhost:8080/test/cookie?browser=chrome,获得的输出以下:

 

1

2

 

存在session,browser=360

JSESSIONID : 320C21A645A160C4843D076204DA2F40

证明了一点,存放在客户端的Cookie的确是存在安全问题的,咱们使用360的JSESSIONID“骗”过了服务器。毕竟,服务器只能经过Cookie中的JSESSIONID来辨别身份。(这提示咱们不要在公共场合保存Cookie信息,如今的浏览器在保存Cookie时一般会让你肯定一次)

然而我使用以上Demo报出了Redis错误!通过排查和浏览官网找到了答案。

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'enableRedisKeyspaceNotificationsInitializer' defined in org.springframework.session.data.redis.config.annotation.web.http.RedisHttpSessionConfiguration: Invocation of init method fail ed; nested exception is java.lang.IllegalStateException: Unable to configure Redis to keyspace notifications. See http://docs.spring.io/spring-session/docs/current/reference/html5/#api-redisoperationssessionrepository-sessiondestroyedevent at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1578)

 

完美解决!!!

相关文章
相关标签/搜索