SpringSecurity中的集群会话Session存在的问题以及解决方案(保持、复制、共享)

        在初步了解Session的时候,我们就知道Session通常是保存在服务器的内存当中的。每一次客户访问都会携带SessionId,然后在服务器的内存中寻找。虽然这种方式简单快捷,但是仍存在比较明显的缺点。服务器的内存是有限的,所以留给Session的空间不多,因此一旦用户的访问量比较多时内存就会捉襟见肘。而且因为session是存在内存当中,并不是持久化存储,就算服务器性能特别好,但是也不免存在服务器的异常停止和重启,这个时候就会导致会话状态的丢失。

SpringSecurity中的集群会话Session存在的问题以及解决方案(保持、复制、共享)_第1张图片

        可能你会说采用集群部署,多布置几台服务器就可以解决上面存在的问题,如上图所示的网络结构的集群部署。在这种情况下,用户的请求会首先到负载均衡的服务器上,再根据不同负载策略将请求分发到后面的服务器上。此时,就有这样的可能性发生,用户的登录操作是在Server1上完成的,因此Session的信息是缓存在Server1上,但是Server2和Server3是不知情的,如果同一个用户再一次访问若请求被分配到了Server2和Server3上,就找不到Session,就会要求用户重新登录。这就是集群环境中的会话状态不同步的问题。

集群会话方案

       针对于集群环境中会话存在Session共享的问题,目前常见的方案主要分为以下三类:

        1) Session保持

        2) Session复制

        3) Session共享

        Session保持

         所谓的Session保持也叫做Session粘滞,采用这种方式需要与上面的负载均衡策略相结合。这里的粘滞和保持其实指的是,用户第一次请求被负载均衡LB(Load Balance)服务器转发到Server1上,设置为粘滞和保持后,用户后面的请求始终都是只通过Server1这台服务器,变相的把用户和Server1服务器绑定在一起,这就是所谓的保持和粘滞。实现这种的方式也很简单,通常使用的IP哈希的负载均衡策略将来自相同客户端的请求转发到相同的服务器上。

# upstream代表负载均衡的策略
upstream session_keep{
    ip_hash;
    server xxxx:8080;
    server xxxx:8080;
    # down代表server暂时不参与负载均衡
    server xxxx:8080 down;
}

       上面是Nginx环境下实现Session保持的方式,可以看到非常简便,不需要对Session做任何处理,也达到一定程度上的集群会话。但是通过此方式实现,仍然避免不了访问的服务器发生故障时,用户会被转移到另外一个服务器上。

       此外采用会话保持的方式仍有一个很明显的错误——通过IP哈希实现将请求转发到服务器上,但是某家公司或者组织使用的都是同一个IP出口,那么这家公司或组织的员工都会被分发到相同服务器上,而另外一个IP只有一个或者几个用户同时使用。这种情况下就很明显的存在一定程度的负载失衡,且如果公司的员工或组织使用的人数比较多,对服务器的压力也是存在一定的挑战性。

        Session复制

        此方式顾名思义,就是在集群服务器之间同步复制Session数据,通俗的就是说,用户A在Server1上访问后生成的Session信息,会定时的复制粘贴到Server2、Server3这些其他服务器上去,从而确保集群上的各个实例之会话状态的一致性。这种方式的缺点也是毫无疑问的,任意一个服务器的Session数据发生改变,都会拷贝到其他服务器上,若服务器实列很多的情况下这种同步不仅仅会消耗数据带宽,还会占用大量的资源,而且效率低,也有很严重的延迟。

        实现Session复制的方式也很简单,需要借助于Tomcat集群配置。即对每个tomcat节点的server.xml进行配置。具体配置信息如下



	
        
		
        				 
		
               
			
		    
              
			
            
					  
			
				
			
        
            
			

             
			
		
		
        
		
		
        

        
		

         
		
 

        Session共享

     SpringSecurity中的集群会话Session存在的问题以及解决方案(保持、复制、共享)_第2张图片

        与前面两种解决方案相比,Session共享的实用性和可靠性就比较高。Session共享是指将session从服务器中抽离出来,集中存储到独立的数据容器中,并由各个服务器之间共享。目前比较主流的方案是将各个服务器之间需要共享的Session数据,保存到一个公共的地方,例如Redis。

        上图展示了Session共享下的服务器网络架构图。由于所有服务器实例单点存取Session,因此集群不同步的问题自然也就不存在,而且独立的数据容器容量相较于服务器内存大很多。另外,与服务本身分离、可持久化等特性使得会话状态不会因为服务停止而丢失。但是由于独立的数据容器增加了网络交互,数据容器的读/写性能、稳定性以及网络I/O速度都成为性能的瓶颈。所以推荐在内网环境下,高可用的部署的Redis服务器是最佳的选择。Redis是基于内存的特性让它拥有极高的读/写性能,高可用部署不仅降低了网络I/O损耗,还提高了稳定性。

        如果想把Session共享到Redis中,可由开发者自己实现,但是这种情况下需要考虑如何得到最优存取结构、如何准群清理过期会话以及整合WebSocket等问题。所以才有了Spring Session专门解决集群会话问题。

        这里就简单介绍一下基于Redis整合Spring和Spring Session。

         首先,需要添加Spring Session的依赖和Redis的相关依赖。


        
        
            org.springframework.session
            spring-session-core
        

        
        
            org.springframework.session
            spring-session-data-redis
        

        
        
            org.springframework.boot
            spring-boot-starter-data-redis
        

        之后就可以配置Spring Session,主要是为了Spring Security提供集群支持的会话注册表即SpringSessionBackedSessionRegistry。

// 启用基于Redis的HttpSession
@EnableRedisHttpSession
public class HttpSessionConfig{
    
    // 提供Redis连接
    @Bean
    public RedisConnectionFactory connectionFactory(){
        return new JedisConnectionFactory();
    }
    
    @Autowired
    private FindByIndexNameSessionRepository sessionRepository;
    
    // SpringSessionBackedSessionRegistry 是session为Spring Security提供的
    // 主要用于集群环境下控制会话并发的会话注册表实现
    @Bean
    public SpringSessionBackedSessionRegistry sessionRegistry(){
        return new SpringSessionBackedSessionRegistry(sessionRepository);
    }
    
    // HttpSession事件监听,改用session提供的会话注册表
    @Bean
    public HttpSessionEventPublisher httpSessionEventPublisher(){
        return new HttpSessionEventPublisher();
    }
}

        最后将新的会话注册表提供给SpringSecurity即可。

SpringSecurity中的集群会话Session存在的问题以及解决方案(保持、复制、共享)_第3张图片

 其他的方案

        除了上面所说的三种解决方案外,还有其他的方式实现,这里我只举其中的一种——使用Token代替Session。Token是指访问资源的令牌凭据,用于检验请求的合法性,使用于前后端分离的项目。该方式也能实现记住当前用户等信息,常用的就是基于Json Web Token(JWT)认证授权机制,而且使用Token可以避免CSRF攻击,且完美的契合移动端的需求。

参考文章:

Spring Security系列教程21--会话管理之实现集群会话tomcat 集群(1)总结tomcat的server.ml配置cluster的方式,以及Tomcat集群session共享失败的解决方法

你可能感兴趣的:(Java,Spring,Security,Spring,Session,Tomcat,Session同步,分布式Session)