一边败、為什么需要session共享
HttpSession是由servelet容器進(jìn)行管理的挡鞍。而我們常用的應(yīng)用容器有 Tomcat/Jetty等法褥, 這些容器的HttpSession都是存放在對應(yīng)的應(yīng)用容器的內(nèi)存中丹禀,在分布式集群的環(huán)境下摩疑,通常我們使用Nginx或者LVS危融、Zuul等進(jìn)行反向代理和負(fù)載均衡,因此用戶請求是由一組提供相同服務(wù)的應(yīng)用來進(jìn)行處理雷袋,而用戶最終請求到的服務(wù)由Nginx和LVS吉殃、Zuul進(jìn)行確定辞居。
那么問題就來了,我們怎樣保證多個相同的應(yīng)用共享同一份session數(shù)據(jù)蛋勺?對于這種問題Spring為我們提供了Spring Session進(jìn)行管理我們的HttpSession瓦灶。
二、基礎(chǔ)Spring Boot配置Spring Session
1.添加Spring session的包抱完,而Spring session 是將HttpSession存放在Redis中贼陶,因此需要添加Redis的包。我們這里是用了Spring boot進(jìn)行配置Redis巧娱。
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session</artifactId>
<version>1.3.1.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-redis</artifactId>
<version>1.3.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-data-redis</artifactId>
<version>1.2.2.RELEASE</version>
<type>pom</type>
</dependency>
2碉怔、啟動類使用@EnableRedisHttpSession注解進(jìn)行配置啟用使用Spring session
@SpringBootApplication
@MapperScan(basePackages = "com.engine56.container.common.mapper")
@EnableTransactionManagement
public class ContainerApplication {
public static void main( String[] args ){
new SpringApplicationBuilder(ContainerApplication.class).web(true).run(args);
}
3、配置我們的Redis鏈接禁添,我們這里使用的是Spring Boot作為基礎(chǔ)進(jìn)行配置撮胧,因此我們只需要在YML或者Properties配置文件添加Redis的配置即可。此處在application.properties中配置
spring.redis.database=0
# Redis服務(wù)器地址
spring.redis.host=127.0.0.1
# Redis服務(wù)器連接端口
spring.redis.port=6379
# Redis服務(wù)器連接密碼(默認(rèn)為空)
spring.redis.password=123456
# 連接池最大連接數(shù)(使用負(fù)值表示沒有限制)
spring.redis.pool.max-active=8
# 連接池最大阻塞等待時間(使用負(fù)值表示沒有限制)
spring.redis.pool.max-wait=-1
# 連接池中的最大空閑連接
spring.redis.pool.max-idle=8
# 連接池中的最小空閑連接
spring.redis.pool.min-idle=0
# 連接超時時間(毫秒)
spring.redis.timeout=0
4老翘、在controller編寫代碼
@GetMapping("/session")
public String test(HttpServletRequest request){
HttpSession session = request.getSession();
UUID uid = (UUID) session.getAttribute("uid");
String msg = "拿到了session!";
if (uid == null) {
uid = UUID.randomUUID();
session.setAttribute("uid", uid);
session.setAttribute("userinfo","張三芹啥,男,12歲");
msg="沒拿到session";
}else{
return msg+" ::: "+session.getAttribute("userinfo");
}
return msg;
}
5铺峭、測試
將項目用兩個不同端口啟動墓怀,用第一個端口訪問后,用第二個端口再訪問卫键,看是否拿到session傀履。
測試結(jié)果:第一次訪問輸出:沒拿到session;第二次訪問輸出:拿到了session永罚!張三啤呼,男,12歲呢袱。
三官扣、SpringSession與shiro集成
1、首先要了解springSession實(shí)現(xiàn)原理
- 通過@EnableRedisHttpSession可以知道羞福,Spring Session是通過RedisHttpSessionConfiguration類進(jìn)行配置惕蹄,該類是用于創(chuàng)建一個過濾SessionRepositoryFilter
擴(kuò)展知識:Spring Session提供了3種方式存儲session的方式。
@EnableRedisHttpSession-存放在緩存redis
@EnableMongoHttpSession-存放在Nosql的MongoDB
@EnableJdbcHttpSession-存放數(shù)據(jù)庫 - 此filter放在所有filter之前治专,接管session管理卖陵。
- 如何獲取getSession:
先檢查是不是已經(jīng)有session了。如果有的話张峰,就將其返回泪蔫,
否則的話,它會檢查當(dāng)前的請求中是否有session id喘批。
如果有的話撩荣,將會根據(jù)這個session id铣揉,從它的SessionRepository中加載session。
如果session repository中沒有session餐曹,或者在當(dāng)前請求中逛拱,沒有當(dāng)前
session id與請求關(guān)聯(lián)的話,那么它會創(chuàng)建一個新的session台猴,并將其
持久化到session repository中 - 如何存儲session
請求時朽合,先獲取當(dāng)前session,不為空時即保存session饱狂。保存后曹步,判斷
當(dāng)前請求中的sessionId是否與當(dāng)前sessionId一致,若不一致休讳,則將當(dāng)
前sessionId保存至cookie箭窜。
2、shiro配置
@Bean(name = "securityManager")
public DefaultWebSecurityManager getDefaultWebSecurityManager(EgRealm myShiroRealm) {
DefaultWebSecurityManager dwsm = new DefaultWebSecurityManager();
dwsm.setRealm(myShiroRealm);
//<!-- 用戶授權(quán)/認(rèn)證信息Cache, 采用EhCache 緩存 -->
dwsm.setCacheManager(getEhCacheManager());
return dwsm;
}
ServletContainerSessionManager:DefaultWebSecurityManager使用的默認(rèn)實(shí)現(xiàn)衍腥,用于Web環(huán)境,其直接使用Servlet 容器的會話纳猫;
故婆咸,不需要再額外配置,spring-session直接為shiro所用芜辕。
三尚骄、nginx實(shí)現(xiàn)負(fù)載均衡
以上實(shí)現(xiàn)了session共享后,如何做到負(fù)載均衡就要靠nginx了侵续,配置如下:
(具體需要如何配置看項目業(yè)務(wù)需要了)
upstream blank {
server 127.0.0.1:3000 weight=10;
server 127.0.0.1:3001 weight=1;
}
server {
listen 8000;
server_name localhost;
location ~^/engine56{
proxy_pass http://blank;//注意:blank要和上面upstream后的名稱一致倔丈。
}
location / {
root D:\xxxx\xxxxx;
index index.html index.htm;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}