SpringBoot 分佈式session共享方案(並且可實現session在多個項目中共享)

前言

單機環境下我們Session是存儲在應用服務的內存中,但是在分佈式環境 下,這種存儲在應用服務器內存的方案顯然不能實現session共享。本次我們將介紹spring-session實現分佈式環境下Session共享方案,Session信息存儲在redis中。

版本

spring-session 2.1.4.RELEASE

實現步驟

1. 引入依賴

	<!--redis的依賴-->
		<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>

spring-session-data-redis 依賴的作用是引入將Session寫入redis的工具類。

2. 添加Session配置類

@Configuration
@EnableRedisHttpSession(maxInactiveIntervalInSeconds = -1)
public class SessionConfig {}

maxInactiveIntervalInSeconds 屬性用來設置Session失效的時間,單位是秒,-1表示永不失效。如果配置1小時失效則是60 * 60 * 1

3. 配置redis

spring.redis.host=192.168.226.111
spring.redis.database=0
spring.redis.port=6379

測試步驟

將同一個應用分別部署到兩臺服務上,然後通過nginx 實現負載均衡。
如下,我將應用分別部署在192.168.226.111和192.168.226.112兩臺服務器上,然後配置nginx 做了轉發,配置如下:

upstream bootDemo {
	server 192.168.226.111:8080;
	server 192.168.226.112:8080;
} 
server {
    listen       80;
    server_name  shoptest.jss.com.cn;
	 location /spring-boot-session/ {
            proxy_pass http://bootDemo/spring-boot-session/;
        }
}

調用登錄接口,接口請求落到了192.168.226.112服務器上,由於是第一次請求,所以Tomcat會給當前會話生成一個Session。該Session中中保存了sessionId,creationTime,lastAccessedTime等信息。其中sessionId是4cebb851-b9b2-488e-9569-9ace9298bb67我們手動設置到Session中的用戶信息是{“password”:“123123”,“userName”:“ceshi”}。
然後,我們我們通過 keys "*session*"指令可以找到 當前存入的用戶如下圖所示:在這裏插入圖片描述
相關key的說明如下:

        //存儲 Session 數據,數據類型hash,可以使用type查看
        Key:spring:session:sessions:4cebb851-b9b2-488e-9569-9ace9298bb67

        //Redis TTL觸發Session 過期。(Redis 本身功能),數據類型:String
        Key:spring:session:sessions:expires:4cebb851-b9b2-488e-9569-9ace9298bb67

        //執行 TTL key ,可以查看剩餘生存時間
        //定時Job程序觸發Session 過期。(spring-session 功能),數據類型:Set
        Key:spring:session:expirations:133337740000

說明:前面的spring:session是Spring Session保存Session信息的前綴名稱。
然後,我們請求獲取用戶信息接口,接口請求到了192.168.226.112服務器,可以獲取到登錄用戶信息。
在這裏插入圖片描述

----------------------------------------------------------------完美的分割線-----------------------------------------------------------------------------------

新場景

今天項目中碰到一個需求,A項目和B項目在同一個域名下,在A項目上登錄之後,在B項目上就不需要在登錄了,這要實現同域名下不同項目間的session共享。
然而,使用這個配置,

@Configuration
@EnableRedisHttpSession(maxInactiveIntervalInSeconds = -1)
public class SessionConfig {}

並不能實現,原因是sessionid是放在cookie中緩存的,而這個cookie值的作用域(path)是指定項目名的,如下圖所示:
在這裏插入圖片描述
所以同域名下不同項目名的項目是不能共享同一個session的,要想共享的話,只有修改Path,將其改到根目錄下/。那麼該怎麼修改呢?首先,我們來看看spring-session是如何管理cookie的。
管理cookie的入口是SessionRepositoryFilter過濾器,而SessionRepositoryFilter過濾器中定義了httpSessionIdResolver屬性,這個屬性是CookieHttpSessionIdResolver類的實例,而在CookieHttpSessionIdResolver類中又定義了cookieSerializer屬性,這個屬性是DefaultCookieSerializer類的實例。那麼我們最終就可以知道,cookie是通過DefaultCookieSerializer類來管理的。那麼我們只需要重新設置DefaultCookieSerializer值就可以了。
那麼怎麼設置呢?通過看DefaultCookieSerializer類中設置CookiePath的方法getCookiePath,我們知道當cookiePath爲空是默認取項目名,當cookiePath不爲空時就取cookiePath。

	private String getCookiePath(HttpServletRequest request) {
		if (this.cookiePath == null) {
			return request.getContextPath() + "/";
		}
		return this.cookiePath;
	}

所以,我們只需要在SessionConfig類中重新設置DefaultCookieSerializer的cookiePath屬性即可。設置如下:

@Configuration
@EnableRedisHttpSession(maxInactiveIntervalInSeconds = -1)
public class SessionConfig {
    @Bean
    public DefaultCookieSerializer defaultCookieSerializer() {
        DefaultCookieSerializer defaultCookieSerializer = new DefaultCookieSerializer();
        defaultCookieSerializer.setCookiePath("/");
        return defaultCookieSerializer;
    }
}

設置後的效果如下圖所示:
在這裏插入圖片描述
在這裏插入圖片描述
同樣的B項目也是同樣的設置

源代碼地址:

https://github.com/XWxiaowei/spring-boot-session-demo

參考文章

https://blog.csdn.net/sayyy/article/details/104199215?tdsourcetag=s_pctim_aiomsg

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章