在我方供應鏈項目分布式部署的環境下,需要在統一網關服務中管理訪問的Session,即無論訪問請求路由到哪一個網關服務環境,使用的都是相同的HttpSession,這樣就保證了在用戶登錄之后,能夠使用統一的Session來處理鑒權和其他邏輯,這對於分布式系統的用戶會話管理是必要的。為了能夠達到這個目的,我們引入了SpringSession。
SpringSession是什么
SpringSession是Spring框架大集合下的一個子組件,使用Redis來備份Web服務訪問生成的(被加工過的)HttpSession,當你在使用SpringBoot框架的時候,你能夠很方便的集成和使用它。
SpringSession如何使用
1、增加依賴
當你在使用SpringBoot框架並采用maven來管理依賴庫時,你只需增加下面的依賴:
1 <dependencies> 2 <!-- ... --> 3 4 <dependency> 5 <groupId>org.springframework.session</groupId> 6 <artifactId>spring-session-data-redis</artifactId> 7 </dependency> 8 </dependencies>
幸運的是,當你在使用SpringBoot框架時,你不需要手動指定依賴的具體版本,它會幫你自動管理。
2、SpringBoot配置項
得益於SpringBoot的自動配置管理支持,在設置使用Redis備份SpringSession時,我們只需要在application.properties增加如下內容:
# Session store type.
spring.session.store-type=redis
在如上的配置驅動下,我們再在SpringBoot的配置管理下增加@EnableRedisHttpSession注解,它會自動創建一個名稱為SpringSessionReposityFilter且實現了Filter接口的(過濾器)類,這個過濾器將負責替換HttpSession的實現為SpringSession。
更多關於SpringSession的配置如下:
# Session timeout. If a duration suffix is not specified, seconds is used. server.servlet.session.timeout= # Sessions flush mode. spring.session.redis.flush-mode=on-save # Namespace for keys used to store sessions. spring.session.redis.namespace=spring:session
3、配置Redis連接
其實SpringBoot會自動創建一個叫做RedisConnectionFactory的類來管理SpringSession與Redis服務的連接,默認是連接到localhost端口為6379的Redis服務,但是在生產環境中,那就必須將這種與Redis服務器連接的配置進行自定義更改,你可以按照如下代碼清單所例舉的方式將配置追加到application.properties文件中:
# Redis server host. spring.redis.host=localhost # Login password of the redis server. spring.redis.password= # Redis server port. spring.redis.port=6379
當然,如果你的SpringBoot中自有管理與Redis服務器的連接,你同樣可以在你的Configuration將這個連接復用給這里的配置,比如你使用JedisShardInfo。
以下是使用JedisShardInfo的代碼參考:

1 @Bean("jedisShardInfo") 2 public JedisShardInfo jedisShardInfo(@Value("${jedis.redis.uri}") String uri, 3 @Value("${catalog.redis.tesla}") String redisCatalog) { 4 String redisUri = uri; 5 redisUri = getRedisUri(redisCatalog, redisUri); 6 return new JedisShardInfo(redisUri); 7 } 8 9 private String getRedisUri(String redisCatalog, String redisUri) { 10 if(!StringUtils.isEmpty(redisCatalog)) { 11 log.info("===> Redis use unified configuration."); 12 DataSourceInstanceConfig dbInstanceConfig = DBLoader.getDataSourceInstanceConfig(redisCatalog); 13 RedisServerInfo serverInfo = dbInstanceConfig.getServer(RedisServerInfo.class).get(0); 14 redisUri = REDIS_URI_PREFIX.concat(serverInfo.getPassword()).concat("@") 15 .concat(serverInfo.getHost()).concat(":") 16 .concat(serverInfo.getPort()).concat("/") 17 .concat(serverInfo.getDb()); 18 log.info("===> redis_uri: {}", redisUri); 19 } 20 return redisUri; 21 } 22 23 @Configuration 24 @EnableRedisHttpSession(maxInactiveIntervalInSeconds = 172800) 25 public class HttpSessionConfig { 26 @Bean 27 public static ConfigureRedisAction configureRedisAction() { 28 return ConfigureRedisAction.NO_OP; 29 } 30 31 @Bean 32 public JedisConnectionFactory connectionFactory(@Autowired @Qualifier("jedisShardInfo") JedisShardInfo jedisShardInfo){ 33 return new JedisConnectionFactory(jedisShardInfo); 34 } 35 }
4、Servlet容器初始化
在SpringBootConfiguration的配置驅動下,自動創建的SpringSessionRepositoryFilter將負責替換系統的HttpSession為SpringSession並保存於redis中,為了這個攔截器能夠發揮作用,Spring需要將這個過濾器納入配置管理,最后我們還需要確保Servlet容器能夠正確的使用這個攔截器攔截到所有請求,幸運的是,SpringBoot為我們顧及到了所有的這些步驟。
SpringSession的實現原理是什么
和我們使用Tomcat的HttpSession不同的是,我們將這個會話內容持久化到了Redis中。SpringSession將HttpSession替換為一種新的實現,並將它保存到了Redis中,也就是當我們的SpringSecurity的SecurityContextPersistenceFilter在保存了SecurityContext到HttpSession之后,就會觸發這個替換機制保存到Redis中。
當我們發起訪問請求的時候,系統會創建一個HttpSession,而SpringSesion會創建一個名稱為SESSION的cookie放到你瀏覽器的中,這個cookie的內容包含了你session的ID,你可以在你的Chrome瀏覽器控制台中查看到。
於此同時,你也可以在你的redis服務器中看到,找到以application.properties中配置的命名空間開頭的key值,然后你會看到包含和瀏覽器上看到的SESSION相同內容的key,當然你可以在redis服務器中使用redis-cli命令去查看或者刪除它。
當你瀏覽器每次發起Http請求到服務端時,都會攜帶這個cookie內容,服務端SpringSession接受到這個cookie值之后就自動去redis中找到相同的會話Session,由此來識別出是同一個用戶進行的訪問。
參考資料
1、https://docs.spring.io/spring-session/docs/current/reference/html5/guides/boot-redis.html
2、http://blog.didispace.com/spring-session-xjf-2/