使用 HTTP 的链接是无状态的,所以为了应对须要状态的服务例如用户登陆,诞生了适合保存状态的设计-会话(session),本文就来探讨一下会话。html
Spring Mvc 中使用会话很简单,在控制器类的方法参数列表中,直接编写 HttpSession
类型的参数,或者参数列表中编写 HttpServletRequest
类,而后使用 getSession()
方法获取会话。redis
下面是使用会话的简单例子,第一次访问时会建立一个无数据的会话,所以获取到的 access
属性为 null ,而当不是第一次访问时,因为属性不为 null 会获得 "NOT THE FIRST TIME ACCESS" 。spring
@RestController public class DemoController { private static final String ACCESS = "access"; @RequestMapping("/") public String index(HttpSession session) { // or `index(HttpServletRequest req)` // then `HttpSession session = request.getSession();` if (session.getAttribute(ACCESS) == null) { session.setAttribute(ACCESS, true); return "FIRST TIME ACCESS"; } return "NOT THE FIRST TIME ACCESS"; } }
注意:因为用 Mock Mvc 测试获取不到第一次请求 Cookies,所以没法模拟获得正确结果,请使用浏览器或者请求工具测试。数据库
上面例子展现了会话的简单使用,其中 HttpSession
接口是 servlet 的标准,而 Spring Mvc 中的会话默认使用 Tomcat 的实现。下面来介绍几个经常使用方法,更多方法使用请参考这篇文章。浏览器
Object getAttribute(String)
方法用来获取会话的属性,若不存在则返回 nullvoid setAttribute(String, Object)
方法用来设置会话的属性void removeAttribute(String)
方法用来删除会话的属性void setMaxInactiveInterval(int)
方法用来设置会话失效时间,单位为秒,设置小于等于零的数则会话永不过时void invalidate()
手动使会话失效并清理会话数据会话的生命周期分别为建立、失效和建立与失效之间,而会话监听器是为了知足会话生命周期中触发相应事件的须要,HttpSessionListener
和 HttpSessionAttributeListener
两个监听器接口分别知足了会话的各个生命周期。使用监听器只需实现这些接口而后标注 @WebListener
注解便可,下面会有实现的例子。安全
HttpSessionListener
接口能够算是针对会话的监听器接口,由于它的两个方法分别在会话建立和失效时调用,下面为一个简单的例子,参数列表中 HttpSessionEvent
类能够用 getSession
获取会话。服务器
@WebListener public class SessionListener implements HttpSessionListener { public void sessionCreated(HttpSessionEvent event) { // ... } public void sessionDestroyed(HttpSessionEvent event) { // ... } }
与 HttpSessionListener
监听器接口接管会话生命周期的建立与失效不一样,HttpSessionAttributeListener
监听器接口负责会话属性的建立、销毁与替换,下面为该监听器的简单例子,参数列表中 HttpSessionBindingEvent
类除了能够用 getSession
获取会话,最主要的是可用 getName
和 getValue
分别获取属性的名字和值。session
@WebListener public class SessionListener implements HttpSessionBindingListener { public void attributeAdded(HttpSessionBindingEvent event) { // ... } public void attributeRemoved(HttpSessionBindingEvent event) { // ... } public void attributeReplaced(HttpSessionBindingEvent event) { // ... } }
上面的例子只是编写了监听器的实现,为了使得监听器在项目里生效,还必须在启动类或者配置类上标注 @ServletComponentScan
来扫描这些属于 servlet 组件的监听器,例以下面在配置类上启动 servlet 组件扫描。app
@Configuration @ServletComponentScan // enable scan servlet component public class ApplicationConf { // ... }
如果有多台 Web 服务器提供不一样的服务,且要求属于同一会话,上面的单机会话例子就没法知足要求,因而就有了分布式会话便可以共享会话数据。分布式
利用 Spring Session 就能够实现分布式会话,而 Spring Session 的实现可依赖关系数据库或内存数据库,下面例子为 Spring Boot 中导入基于 Redis 实现的 Spring Session 的依赖。
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <dependency> <groupId>org.springframework.session</groupId> <artifactId>spring-session-data-redis</artifactId> </dependency> <dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> </dependency>
接着在 Spring Boot 的属性配置文件中,添加以下的属性便可,而对于会话的使用和单机会话操做是同样的。
spring: session: store-type: redis redis: host: 127.0.0.1 port: 6379
关于会话安全问题,因为了解知识尚浅,暂且不作探讨,后续会补充该部分。