SpringBoot 分布式session共享方案(并且可實(shí)現(xiàn)session在多個(gè)項(xiàng)目中共享)
前言
單機(jī)環(huán)境下我們Session是存儲(chǔ)在應(yīng)用服務(wù)的內(nèi)存中,但是在分布式環(huán)境
下,這種存儲(chǔ)在應(yīng)用服務(wù)器內(nèi)存的方案顯然不能實(shí)現(xiàn)session共享。本次我們將介紹spring-session實(shí)現(xiàn)分布式環(huán)境下Session共享方案,Session信息存儲(chǔ)在redis中。
版本
spring-session 2.1.4.RELEASE
實(shí)現(xiàn)步驟
- 引入依賴
<!--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 屬性用來設(shè)置Session失效的時(shí)間,單位是秒,-1表示永不失效。如果配置1小時(shí)失效則是60 * 60 * 1。
3. 配置redis
spring.redis.host=192.168.226.111
spring.redis.database=0
spring.redis.port=6379
測(cè)試步驟
將同一個(gè)應(yīng)用分別部署到兩臺(tái)服務(wù)上,然后通過nginx 實(shí)現(xiàn)負(fù)載均衡。
如下,我將應(yīng)用分別部署在192.168.226.111和192.168.226.112兩臺(tái)服務(wù)器上,然后配置nginx 做了轉(zhuǎn)發(fā),配置如下:
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/;
}
}
調(diào)用登錄接口,接口請(qǐng)求落到了192.168.226.112服務(wù)器上,由于是第一次請(qǐng)求,所以Tomcat會(huì)給當(dāng)前會(huì)話生成一個(gè)Session。該Session中中保存了sessionId,creationTime,lastAccessedTime等信息。其中sessionId是4cebb851-b9b2-488e-9569-9ace9298bb67我們手動(dòng)設(shè)置到Session中的用戶信息是{“password”:“123123”,“userName”:“ceshi”}。
然后,我們我們通過 keys "session"指令可以找到 當(dāng)前存入的用戶如下圖所示:
相關(guān)key的說明如下:
//存儲(chǔ) Session 數(shù)據(jù),數(shù)據(jù)類型hash,可以使用type查看
Key:spring:session:sessions:4cebb851-b9b2-488e-9569-9ace9298bb67
//Redis TTL觸發(fā)Session 過期。(Redis 本身功能),數(shù)據(jù)類型:String
Key:spring:session:sessions:expires:4cebb851-b9b2-488e-9569-9ace9298bb67
//執(zhí)行 TTL key ,可以查看剩余生存時(shí)間
//定時(shí)Job程序觸發(fā)Session 過期。(spring-session 功能),數(shù)據(jù)類型:Set
Key:spring:session:expirations:133337740000
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
說明:前面的spring:session是Spring Session保存Session信息的前綴名稱。
然后,我們請(qǐng)求獲取用戶信息接口,接口請(qǐng)求到了192.168.226.112服務(wù)器,可以獲取到登錄用戶信息。
----------------------------------------------------------------完美的分割線-----------------------------------------------------------------------------------
新場(chǎng)景
今天項(xiàng)目中碰到一個(gè)需求,A項(xiàng)目和B項(xiàng)目在同一個(gè)域名下,在A項(xiàng)目上登錄之后,在B項(xiàng)目上就不需要在登錄了,這要實(shí)現(xiàn)同域名下不同項(xiàng)目間的session共享。
然而,使用這個(gè)配置,
@Configuration
@EnableRedisHttpSession(maxInactiveIntervalInSeconds = -1)
public class SessionConfig {}
并不能實(shí)現(xiàn),原因是sessionid是放在cookie中緩存的,而這個(gè)cookie值的作用域(path)是指定項(xiàng)目名的,如下圖所示:
所以同域名下不同項(xiàng)目名的項(xiàng)目是不能共享同一個(gè)session的,要想共享的話,只有修改Path,將其改到根目錄下/。那么該怎么修改呢?首先,我們來看看spring-session是如何管理cookie的。
管理cookie的入口是SessionRepositoryFilter過濾器,而SessionRepositoryFilter過濾器中定義了httpSessionIdResolver屬性,這個(gè)屬性是CookieHttpSessionIdResolver類的實(shí)例,而在CookieHttpSessionIdResolver類中又定義了cookieSerializer屬性,這個(gè)屬性是DefaultCookieSerializer類的實(shí)例。那么我們最終就可以知道,cookie是通過DefaultCookieSerializer類來管理的。那么我們只需要重新設(shè)置DefaultCookieSerializer值就可以了。
那么怎么設(shè)置呢?通過看DefaultCookieSerializer類中設(shè)置CookiePath的方法getCookiePath,我們知道當(dāng)cookiePath為空是默認(rèn)取項(xiàng)目名,當(dāng)cookiePath不為空時(shí)就取cookiePath。
private String getCookiePath(HttpServletRequest request) {
if (this.cookiePath == null) {
return request.getContextPath() + "/";
}
return this.cookiePath;
}
所以,我們只需要在SessionConfig類中重新設(shè)置DefaultCookieSerializer的cookiePath屬性即可。設(shè)置如下:
@Configuration
@EnableRedisHttpSession(maxInactiveIntervalInSeconds = -1)
public class SessionConfig {
@Bean
public DefaultCookieSerializer defaultCookieSerializer() {
DefaultCookieSerializer defaultCookieSerializer = new DefaultCookieSerializer();
defaultCookieSerializer.setCookiePath("/");
return defaultCookieSerializer;
}
}
設(shè)置后的效果如下圖所示:
同樣的B項(xiàng)目也是同樣的設(shè)置。
作者:碼農(nóng)飛哥
微信公眾號(hào):碼農(nóng)飛哥