Spring Boot 解決方案 - 會話


連接無狀態

使用 HTTP 的連接是無狀態的,因此為了應對需要狀態的服務例如用戶登錄,誕生了適合保存狀態的設計-會話(session),本文就來探討一下會話。

會話的使用

Spring Mvc 中使用會話很簡單,在控制器類的方法參數列表中,直接編寫 HttpSession 類型的參數,或者參數列表中編寫 HttpServletRequest 類,然后使用 getSession() 方法獲取會話。

下面是使用會話的簡單例子,第一次訪問時會創建一個無數據的會話,因此獲取到的 access 屬性為 null ,而當不是第一次訪問時,由於屬性不為 null 會得到 "NOT THE FIRST TIME ACCESS" 。

@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) 方法用來獲取會話的屬性,若不存在則返回 null
  • void setAttribute(String, Object) 方法用來設置會話的屬性
  • void removeAttribute(String) 方法用來刪除會話的屬性
  • void setMaxInactiveInterval(int) 方法用來設置會話失效時間,單位為秒,設置小於等於零的數則會話永不過期
  • void invalidate() 手動使會話失效並清理會話數據

會話監聽器

會話的生命周期分別為創建、失效和創建與失效之間,而會話監聽器是為了滿足會話生命周期中觸發相應事件的需要,HttpSessionListenerHttpSessionAttributeListener 兩個監聽器接口分別滿足了會話的各個生命周期。使用監聽器只需實現這些接口然后標注 @WebListener 注解即可,下面會有實現的例子。

針對會話的監聽器

HttpSessionListener 接口可以算是針對會話的監聽器接口,因為它的兩個方法分別在會話創建和失效時調用,下面為一個簡單的例子,參數列表中 HttpSessionEvent 類可以用 getSession 獲取會話。

@WebListener
public class SessionListener implements HttpSessionListener {
	public void sessionCreated(HttpSessionEvent event) {
		// ...
    }
	public void sessionDestroyed(HttpSessionEvent event) {
		// ...
    }
}

針對會話屬性的監聽器

HttpSessionListener 監聽器接口接管會話生命周期的創建與失效不同,HttpSessionAttributeListener 監聽器接口負責會話屬性的創建、銷毀與替換,下面為該監聽器的簡單例子,參數列表中 HttpSessionBindingEvent 類除了可以用 getSession 獲取會話,最主要的是可用 getNamegetValue 分別獲取屬性的名字和值。

@WebListener
public class SessionListener implements HttpSessionBindingListener {
	public void attributeAdded(HttpSessionBindingEvent event) {
		// ...
	}
	
	public void attributeRemoved(HttpSessionBindingEvent event) {
		// ...
    }
    
    public void attributeReplaced(HttpSessionBindingEvent event) {
		// ...
    }
}

使監聽器生效

上面的例子只是編寫了監聽器的實現,為了使得監聽器在項目里生效,還必須在啟動類或者配置類上標注 @ServletComponentScan 來掃描這些屬於 servlet 組件的監聽器,例如下面在配置類上啟動 servlet 組件掃描。

@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

會話安全問題

關於會話安全問題,由於了解知識尚淺,暫且不做探討,后續會補充該部分。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM