文档章节

从零开始的Spring Session(一)

二十岁以后
 二十岁以后
发布于 2017/09/12 10:41
字数 1522
阅读 74
收藏 1

本文作者:徐靖峰
原文链接:http://t.cn/Rp7xUro
版权归作者所有,转载请注明出处

 

之前项目需要使用Redis管理Session,找了很多的Demo,研究了一天Spring Session源码,感觉有点难继续。突然看到群里Pivotal的周辉大牛发了一篇连接,突然豁然开朗。

 

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

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,他是真正维系客户端和服务端的桥梁。

代码示例

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

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

@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机制是什么。

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

 

1

 

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)

 

完美解决!!!

 

本文转载自:

共有 人打赏支持
二十岁以后
粉丝 231
博文 25
码字总数 23016
作品 0
海淀
架构师
加载中

评论(1)

熊猫你好
熊猫你好
前面写的不过,但是后面是个什么鬼,从 完美解决错误哪里完全是牛头不对马嘴啊,大兄弟
从Spring-Session源码看Session机制的实现细节

去年我曾经写过几篇和 Spring Session 相关的文章,从一个未接触过 Spring Session 的初学者视角介绍了 Spring Session 如何上手,如果你未接触过 Spring Session,推荐先阅读下「从零开始学...

徐靖峰
04/20
0
0
Spring Security 从入门到进阶系列教程

Spring Security 入门系列 《保护 Web 应用的安全》 《Spring-Security-入门(一):登录与退出》 《Spring-Security-入门(二):基于数据库验证》 《Spring-Security-入门(三):密码加密...

小致dad
08/03
0
0
Spring中使用getSession()与通过HibernateTemplate进行数据操作...

在 Spring+Hibernate的集成环境里,如果DAO直接使用HibernateDaoSupport的getSession()方法获取 session进行数据操作而没有显式地关闭该session,那么程序表现为:每个session会打开一个con...

小样
2012/08/02
0
1
eclipse下搭建SSH整合环境(Struts2+Spring+Hibernate+maven)

1,创建一个maven工程,在选择Archetype时选择webapp: 2,下一步配置maven的pom.xml文件,获取依赖的jar包: <!-- struts2核心包 --><dependency> </dependency> <!-- struts2与spring整合的......

徐航
2014/05/08
0
5
spring事务管理错误createSQLQuery is not valid without active transaction

网上扒的,很管用,自己就借此发一下,方便以后看。。。。。。。 原文网址http://blog.csdn.net/yinjian520/article/details/8666695 很多时候我们使用hibernate的session时,都是让session...

技术小牛
2014/04/06
0
0

没有更多内容

加载失败,请刷新页面

加载更多

redis-hash

哈希类型是指健值本身又是一个键值对结构 基本命令: hset key field value 设置值 hget(获取),hdel(删除),hlen(计算field个数),hmget(批量设置),hexists(是否存在),hkeys(获取所有的...

拐美人
14分钟前
1
0
简单的svm例子

数据来源:https://github.com/oumiga1314/Coursera-ML-AndrewNg-Notes/blob/master/code/ex6-SVM/data/ex6data1.mat import pandas as pd import numpy as np import scipy.io as sio impor......

南桥北木
18分钟前
0
0
android 关于View的一些整理

1、Button text的值为英文时,会自动转换成大写。如需取消,设置android:textAllCaps="false" 2、控件的可见性 可以在layout的配置文件中,配置android:visibility属性 调用setVisibility()...

西米小娅
28分钟前
0
0
Spring JDBC数据源分析

Spring数据源分析 分析这样一段代码: package com.jason.spring.datasource.jdbc;import org.springframework.context.support.ClassPathXmlApplicationContext;import org.springframew......

宸明
36分钟前
1
0
FatJar:适用于sdk多module打包和合并多个jar的gradle插件

usage: 1.下载fatJar.gradle放置于project根目录 2.在project的build.gradle中添加依赖和配置: apply from: 'fatJar.gradle'buildscript { dependencies { classpath 'xyz......

SuShine
53分钟前
1
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部