文章目录

  • 一、分布式Session
  • 1.1 分布式Session解决方案
  • 2.2 SpringSession
  • 二、单点登录


一、分布式Session

单体项目中我们使用Session和Cookie保存用户信息,而在分布式系统中单独使用Session和Cookie无法已无法满足要求。一、Session不能跨系统保存,二、Cookie的跨域问题

1.1 分布式Session解决方案

① session 复制

分布式session token_redis


优点:web-server (Tomcat)原生支持,只需要修改配置文件

缺点:

  • session同步需要数据传输,占用大量网络带宽,降低了服务器群的业务处理能力
  • 任意一台web-server保存的数据都是所有web-serversession总和,受到内存限制无法水平扩展更多的web-server
  • 大型分布式集群情况下,由于所有web-server都全量保存数据,所以此方案不可取。

② session 存储在客户端

分布式session token_redis_02


优点:服务器不需存储session,用户保存自己的session信息到cookie中,节省服务端资源

缺点:

  • 每次http请求,携带用户在cookie中的完整信息,浪费网络带宽
  • session数据放在cookie中,cookie有长度限制4K,不能保存大量信息
  • session数据放在cookie中,存在泄漏、篡改、窃取等安全隐患

session 存储在客户端这种方式不可取,不能使用。

③ hash 一致性

分布式session token_单点登录_03


优点:

  • 只需要改nginx配置,不需要修改应用代码
  • 负载均衡,只要hash属性的值分布是均匀的,多台web-server的负载是均衡的
  • 可以支持web-server水平扩展(session同步法是不行的,受内存限制)

缺点:

  • session还是存在web-server中的,所以web-server重启可能导致部分session丢失,影响业务,如部分用户需要重新登录
  • 如果web-server水平扩展,rehashsession重新分布,也会有一部分用户路由不到正确的session

④ 统一存储

分布式session token_单点登录_04


优点:

  • 没有安全隐患
  • 可以水平扩展,数据库/缓存水平切分即可
  • web-server重启或者扩容都不会有session丢失

缺点:增加了一次网络调用,并且需要修改应用代码;如将所有的getSession方法替换为从Redis查数据的方式,redis获取数据比内存慢很多。

2.2 SpringSession

通常我们会选择统一存储的方案,其缺点可以使用SpringSession进行解决。

① 引入依赖

<dependency>
    <groupId>org.springframework.session</groupId>
    <artifactId>spring-session-data-redis</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

② 配置

spring.session.store-type=redis
server.servlet.session.timeout=30m
spring.redis.host=192.168.1.100

③ 主启动类添加注解@EnableRedisHttpSession

④ 存在的问题

现在存在的问题是子域名下会保存cookie,但是在父域名中却没有,我们希望只要在子域名下的cookie,父域名也能感知到。

  • 由于默认使用jdk进行序列化,通过导入RedisSerializer修改为json序列化
  • 并且通过修改CookieSerializer扩大session的作用域至**.test.com
@Configuration
public class TestSessionConfig {

    @Bean // redis的json序列化
    public RedisSerializer<Object> springSessionDefaultRedisSerializer() {
        return new GenericJackson2JsonRedisSerializer();
    }

    @Bean // cookie
    public CookieSerializer cookieSerializer() {
        DefaultCookieSerializer serializer = new DefaultCookieSerializer();
        serializer.setCookieName("GULISESSIONID"); // cookie的键
        serializer.setDomainName("test.com"); // 扩大session作用域,也就是cookie的有效域
        return serializer;
    }
}

使用SpringSession其实还存在Cookie的跨域问题,无法跨级,只能实现二级域名的跨域,多系统使用单点登录可以解决这个问题。

二、单点登录

分布式系统登录解决方案单点登录流程如下:

分布式session token_单点登录_05


详细UML图:

分布式session token_分布式session token_06

拦截器代码:

分布式session token_session_07