引言

由于 HTTP 协议是无状态的,完成操作关闭浏览器后,客户端和服务端的连接就断开了,所以我们必须要有一种机制来保证客户端和服务端之间会话的连续性,也称为认证,最常见的应用场景就是保持用户的登录态。

最基本的认证方式,就是使用 Sesson-Cookie。

30s 图解 Sesson-Cookie 认证

以保持用户登录态为例,Sesson-Cookie 认证的具体步骤如下:

如何用 30s 给面试官讲清楚什么是 Session-Cookie 认证-LMLPHP

1)客户端(浏览器): 向服务器发送登录信息(用户名和密码)来请求登录校验;

2)服务端: 验证登录信息,验证通过后服务器(比如 Tomcat)会自动为此次请求开辟一块内存空间(一个 Session 对象),可以手动将用户信息(比如登录保持时间是否过期)存在 Session 对象中。然后,服务器会自动为这个 Sesson 对象生成一个唯一的标识 sessionID ,并在 HTTP 响应头(Header)的 Set-Cookie:JSESSIONID=XXXXXXX 中设置这个 seesionID

3)客户端: 收到服务端的响应后会解析响应头,从而根据 set-CookiesessonId 保存在本地 Cookie 中,这样,客户端(浏览器)在下次 HTTP 请求时请求头会自动附上该域名下的 Cookie 信息;

4)服务端: 接收客户端请求时会去解析请求头 Cookie 中的 sessonId,然后根据这个 sessonId 去找 Sesson 对象,从而获取到用户信息;

如果遇到禁用 Cookie 的情况,一般的做法就是把这个 sessionID 放到 URL 参数中。这也是经常在面试中会被问到的问题。

附加阅读

Sesson-Cookie 认证伪代码

登录:

如何用 30s 给面试官讲清楚什么是 Session-Cookie 认证-LMLPHP

拦截器:每次请求前去找 Sesson 对象,从而获取到用户信息

如何用 30s 给面试官讲清楚什么是 Session-Cookie 认证-LMLPHP

可以看出来,在一次会话当中,两个请求获取到的 Session 对象实际上是同一个对象。

上面已经提到,服务器是根据 cookie 中的 sessionID 来找到 Session 对象的,但以上代码中我们只是手动将用户数据设置到了 Session 中,并没有出现任何关于 Cookie 的代码(将 SessionId 设置到 Cookie 中)

很明显,这些肯定都是服务器(比如 Tomcat)自动完成的了。在第一次获取 Session 即调用 request.getSession() 的时候,服务器会自动创建一个 Session 对象(Session 是一个集合,并且是一个 Map 集合),并且存入服务器的 Session 集合中以 SessionId 为标识键,也就是说根据 SessionId 即可取到对应 Session 的引用。同时也会创建一个键名为 JSESSIONID 的 Cookie 并且返回给浏览器,该 Cookie 的值即为 SessionId。

这个存储着 SessionId 的 Cookie 会跟着请求上传到服务器,所以说,在同一会话当中,不管哪个请求拿到的都是同一个 Session 对象。

Sesson-Cookie 认证的缺点与解决方案

这种机制在单体应用时代应用非常广泛,但是,随着分布式时代的到来,Session 的缺点也逐渐暴露出来。

举个例子,比如我们有多个服务器,客户端 1 向服务器发送了一个请求,由于负载均衡的存在,该请求被转发给了服务器 A,于是服务器 A 创建并存储了这个 Session

如何用 30s 给面试官讲清楚什么是 Session-Cookie 认证-LMLPHP

紧接着,客户端 1 又向服务器发送了一个请求,但是这一次请求被负载均衡给了服务器 B,而服务器 B 这时候是没有存储服务器 A 的 Session 的,这就导致 Session 的失效。

如何用 30s 给面试官讲清楚什么是 Session-Cookie 认证-LMLPHP

明明用户在上一个界面还是登录的,跳到下一个界面就退出登录了,这显然不合理。

当然了,对此的解决方法其实也有很多种,其实就是如何解决 Session 在多个服务器之间的共享问题

  1. Sesson Replication
  2. Sesson Sticky
  3. Sesson 数据集中存储

Session Replication

这个是最容易想到的,既然服务器 B 没有服务器 A 存储的 Session,那各个服务器之间同步一下 Session 数据不就完了。

如何用 30s 给面试官讲清楚什么是 Session-Cookie 认证-LMLPHP

这种方案存在的问题也是显而易见的:

  1. 同步 Session 数据带来了额外的网络带宽开销。只要 Session 数据有变化,就需要将数据同步到所有其他机器上,机器越多,同步带来的网络带宽开销就越大。

  2. 每台Web服务器都要保存所有 Session 数据,如果整个集群的 Session 数据很多(比如很多人同时访问网站的情况),每台服务器用于保存 Session 数据的内存占用会非常严重。

Session Sticky

从名称也能看出来,Sticky,即让负载均衡器能够根据每次的请求的会话标识来进行请求的转发,保证一个会话中的每次请求都能落到同一台服务器上面

如何用 30s 给面试官讲清楚什么是 Session-Cookie 认证-LMLPHP

存在问题的:

  1. 如果某台服务器宕机或者重启了,那么它上面存储的 Session 数据就丢失了,用户就需要重新进行登陆。

  2. 负载均衡器变为一个有状态的节点,因为他需要保存 Session 到具体服务器的映射,和之前无状态的节点相比,内存消耗会更大,容灾方面会更麻烦。

Session 数据集中存储

将每个服务器的 Session 数据都集中存到外部介质比如 Redis 或者 MySQL 中去,然后所有的服务器都从这个外部介质中拿 Session 就行了

如何用 30s 给面试官讲清楚什么是 Session-Cookie 认证-LMLPHP

存在的问题也很显然:

  • 过度依赖外部存储,如果集中存储 Session 的外部存储机器出问题了,就会直接影响到我们的应用
01-06 09:36