前言
單機環境下我們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