Spring session的学习与应用
session
概述
由于HTTP协议是无状态的协议,一次浏览器和服务器的交互过程就是:
发送请求
浏览器----------------------------->服务器
返回请求
浏览器----------------------------->服务器
这就是一次会话,对话完成后这次会话就结束了,服务器端并不能记住这个人的信息状态,下次再次对话时,服务器不能确定此次会话主人的信息,所以服务器端要记录用户的状态时,就要用某种机制来识别到具体的用户,这个机制就是Session。
Session是服务器为每个用户创建一个会话,存储用户的相关信息,以便多次请求能够定位到同一个上下文。这样,当用户在应用程序的 Web 页之间跳转时,存储在 Session 内的客户信息将会被识别统一上下文,整个用户会话中一直存在下去,会话场景则不会丢失。当用户请求来自应用程序的 Web 页时,如果该用户还没有会话,则 Web 服务器将自动创建一个 Session 对象。当会话过期或被放弃后,服务器将终止该会话。
Web开发中,web-server可以自动为同一个浏览器的访问用户自动创建session,提供数据存储功能。最常见的,会把用户的登录信息、用户信息存储在session中,以保持登录状态。
服务器如何识别特定的客户?
这个时候则需要使用Cookie;
每次HTTP请求的时候,客户端都会发送相应的Cookie信息到服务端。
实际上大多数应用都是用Cookie来实现Session跟踪的,第一次创建Session时,服务端会在HTTP协议中向客户端Cookie中记录一个Session ID,以后每次请求把这个会话ID发送到服务器,这样服务端就会知道客户端是谁了;
如果客户端的浏览器禁用了Cookie,会使用一种叫URL重写的技术来进行session会话跟踪,即每次HTTP交互,URL后面都会被附加上一个诸如sessionId=xxx的参数,服务端据此来识别客户端是谁。
Session会话管理
在Web项目中,Session会话管理时一个很重要的部分,用于存储与记录用户的状态或相关的数据信息;通常情况下Session交由容器(Tomcat)来负责存储和管理,但是如果项目部署到多台Tomcat中,Session管理则会存在很大的问题。如:多台Tomcat之间Session无法共享,运用负载均衡部署的话,用户会出现不确定的退出登录的状况;或一旦Tomcat关闭或重启也会导致session会话失效;因此如果项目部署在多台Tomcat中,就要解决Session共享的问题
Session会话共享方案
- 使用容器扩展插件来实现,比如:基于Tomcat的tomcat-redis-session-manager插件(需要在tomcat中的server.xml中配置,是将session复制到所有服务器中),基于Jetty的jetty-session-redis插件、memcached-session-manager插件;这个方案的好处时对项目来说是透明的,无需改动代码,但是由于过于依赖容器,一旦容器升级或是更换,则以位置又要重新配置;如果所搭集群比较多,此种方法效率不好
- 使用Nginx负载均衡的ip_hash策略实现用户每次访问都绑定到同一台具体的后台Tomcat服务器实现session会话总是存在
- 自己写一套session会话管理工具类,在需要使用会话的时候从自己的工具类中获取,而工具类后端存错可以放到Redis中,这个方案灵活性很好,但是开发需要一些额外的时间;
- 使用框架的会话管理工具,也就是Spring session,这个方案即不依赖tomcat,又不需要改动代码,由Spring session框架为我们提供,可以说是目前比较完美的session共享的解决方案
Spring Session
简介
Spring Session是Spring家族中的子成员,它提供一组api和实现,用于管理用户的session信息;它把servlet容器实现的httpSession替换为spring-session,专注于解决session管理问题,Session信息存储在Redis中,可简单快速且无缝的集成到我们的应用中;
Spring 网址https://spring.io/
Spring Session 的特性:
- 提供用户session管理的api和实现;
- 提供HttpSession,以中立的方式取代web容器的session,比如tomcat中的session;
- 支持集群的session处理,不必绑定到具体的web容器去解决集群下的session共享问题;
使用示例
使用Spring Session管理session开发步奏
- spring MVC的配置
-
添加Spring Session的maven依赖:
<!-- spring session 依赖--> <dependency> <groupId>org.springframework.session</groupId> <artifactId>spring-session</artifactId> <version>1.3.1.RELEASE</version> </dependency> <!-- spring session redis 依赖--> <dependency> <groupId>org.springframework.session</groupId> <artifactId>spring-session-data-redis</artifactId> <version>1.3.1.RELEASE</version> </dependency> <!-- spring-dataa-redis 依赖的JAR配置--> <dependency> <groupId>org.springframework.data</groupId> <artifactId>spring-data-redis</artifactId> <version>1.8.8.RELEASE</version> </dependency> <!-- jedis依赖的JAR配置--> <dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> <version>2.9.0</version> </dependency> <!-- spring web 依赖的JAR配置--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-web</artifactId> <version>4.3.10.RELEASE</version> </dependency> <!-- servlet依赖的JAR配置--> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>3.1.0</version> </dependency> <!-- jsp依赖的JAR配置--> <dependency> <groupId>javax.servlet.jsp</groupId> <artifactId>javax.servlet.jsp-api</artifactId> <version>2.3.1</version> </dependency>
2.在web.xml文件中配置springSessionRepositoryFilter过滤器
全局过滤器<!-- Spring session框架的全局的过滤器--> <filter> <filter-name>springSessionRepositoryFilter</filter-name> <!-- DelegatingFilterProxy代理 springSessionRepositoryFilter类 springSessionRepositoryFilter类是在applicationContext.xml中配置的 RedisHttpSessionConfiguration类中 --> <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class> </filter> <filter-mapping> <filter-name>springSessionRepositoryFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
3.在web.xml文件中加载spring配置文件
-
第一种 监听器启动spring容器(Java)
<context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:applicationContext.xml</param-value> </context-param> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener>
-
第二种 使用spring mvc的DispatcherServlet启动spring容器
<servlet> <servlet-name>springmvc</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>springmvc</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping>
4.Spring配置文件(applicationContext.xml)配置一个RedisHttpSessionConfiguration类(在session的配置文件中配置即可)
<!-- spring注解、bean的处理器 注解的配置 识别注解 --> <context:annotation-config/> <!-- Spring session的配置类 --> <bean class:"org.springframework.session.data.redis.config.annotation.web.RedisHttpSessionConfiguration"> <!--指定session策略--> <!--<property name="httpSessionStrategy" ref="httpSessionStrategy"/>--> <!-- 指定过期时间 单位 秒 --> <property name="maxInactiveIntervalInSeconds" value="1800" /> </bean> <bean id="httpSessionStrategy" class="org.springframework.session.web.http.CookieHttpSessionStrategy"> <!--httpsession策略--> <property name="httpSessionStrategy" ref="httpSessionStrategy"/> </bean> <!-- 采用cookie策略 --> <bean id="httpSessionStrategy" class="org.springframework.session.web.http.CookieHttpSessionStrategy"> <property name="cookieSerializer" ref="cookieSerializer" /> </bean> <bean id="cookieSerializer" class="org.springframework.session.web.http.DefaultCookieSerializer" /> <!--指定cookie的域名及路径--> <property name="cookiePath" value="/"/> <property name="domainName" value="web.com"/> </bean>
5.Spring配置文件配置Spring-data-redis
<!-- 配置jedis连接工厂,用于连接redis --> <bean id="jedisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory"> <property name="hostName" value="${redis.hostName}"/> <property name="port" value="${redis.port}"/> <property name="password" value="${redis.password}"/> <!-- 是否使用连接池 --> <property name="usePool" value="${redis.usePool}"/> <!-- 超时时间 --> <property name="timeout" value="${redis.timeout}"/> </bean> <!-- 读取redis.properties属性配置文件 --> <context:property-placeholder location="classpath:redis.properties"/>
配置redis配置文件
redis.hostName=127.0.0.1 redis.port=6379 redis.password=123456 redis.usePool=true redis.timeout=10000
-
-
springBoot配置
1.maven配置依赖<!--redis 依赖--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <!--sessions 依赖--> <dependency> <groupId>org.springframework.session</groupId> <artifactId>spring-session-data-redis</artifactId> </dependency>
2.配置redis.properties文件
在applications.properties文件中加上:
#同时引用redies.properties文件相当于mvc中<import resource="xxxxx.xml" /> spring.profiles.include=redis
spring.redis.host=localhost spring.redis.port=6379 # spring session使用存储类型,spirngboot默认就是使用redis方式,如果不想用可以填none。 spring.session.store-type=redis #连接超时时间(毫秒) spring.redis.timeout=1800000 #指定cookie的路径 server.servlet.session.cookie.path="/" #指定cookie的域名 server.servlet.session.cookie.domain="web.com"
3.在启动类中加入@EnableRedisHttpSession 注解
4.在需要操作Redis的类中注入redisTemplate,它是由spring自动配置好的@Autowride private RedisTemplate<String,String> redisTemplate; //写代码时最好将key的序列化设置成可读性比较强的 redisTemplate.setKeySerializer(new StringSerializer());
或者
@Autowride private RedisTemplate<Object,Object> redisTemplate;
泛型里只能写<String,String>、<Object,Object>
实际应用
-
同域名下相同项目实现session共享
在同一个域名下,比如:www.myweb.com,同一个项目部署了多台tomcat,此为典型的集群方式 -
同域名下不同项目实现session共享
在同一个域名下有多个不同的项目,如:www.web,com/jiekuan;www.web.com/touzi;
注意:设置Cookie路径为根/上下文
在applicationContext.xml文件的RedisHttpSessionConfiguration类的bean中设置session策略<!-- Spring session的配置类 --> <bean class:"org.springframework.session.data.redis.config.annotation.web.RedisHttpSessionConfiguration"> <!--指定session策略--> <!--<property name="httpSessionStrategy" ref="httpSessionStrategy"/>--> <!-- 指定过期时间 单位 秒 --> <property name="maxInactiveIntervalInSeconds" value="1800" /> </bean> <bean id="httpSessionStrategy" class="org.springframework.session.web.http.CookieHttpSessionStrategy"> <!--httpsession策略--> <property name="httpSessionStrategy" ref="httpSessionStrategy"/> </bean> <!-- 采用cookie策略 --> <bean id="httpSessionStrategy" class="org.springframework.session.web.http.CookieHttpSessionStrategy"> <!-- cookie的存放方式 --> <property name="cookieSerializer" ref="cookieSerializer" /> </bean> <bean id="cookieSerializer" class="org.springframework.session.web.http.DefaultCookieSerializer" /> <!-- 不设置的时候默认的是项目工程下的 --> <!--指定cookie的路径 存放在根路径下--> <property name="cookiePath" value="/"/> </bean>
-
同域名不同二级子域名下的项目实现session共享
同一个根域名但是二级域名不同,如:www.web.com、beijing.web.com、nanjing.web.com
* 设置Cookie路径为根/上下文
同上
* 设置cookie域名根域名web.com<bean id="cookieSerializer" class="org.springframework.session.web.http.DefaultCookieSerializer" /> <!--指定cookie的路径--> <property name="cookiePath" value="/"/> <!--指定cookie的域名--> <property name="domainName" value="web.com"/> </bean>
-
不同根域名下的项目实现session共享
根域名不同 如:www.baidu.com、www.webo.com,要实现一处登录,处处登录,Spring Session不支持,需要采用其它方式。
如:单点登录(Single Sign On,SSO),在多个应用系统中,用户只需要登录一次就可访问所有相互信任的应用系统。
比如阿里的多个业务线之间的登录模式
集群环境下session共享示例