使用 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
關於會話安全問題,因爲了解知識尚淺,暫且不作探討,後續會補充該部分。