Cookie

在我们第一次访问服务器 的时候,服务器会给我们一张身份证 ,我们需要将它保存 好,然后在需要的时候带上他,证明我们的身份。那么客户端该怎么保存身份证?又怎么带上它呢?每次都要这两个操作那能不能不用这么麻烦?

Cookie:保存身份证

好了,Cookie就是客户端数据保存技术。下面详细介绍Cookie的工作过程。

  • 浏览器向服务端的HTTP请求接口发起HTTP请求,服务端在响应处设置cookie
  • 响应回来时,浏览器自动将Cookie保存到浏览器
  • 接下来的一段时间 内,每次向服务端进行请求时都会带上Cookie

HTTP响应头:带回身份证

在图中我们可以看到,当浏览器向服务端发送第一次请求后,将会带回来几个响应头。而带回来的Cookie也在其中,以"响应头:key=value"的形式传回。在上图中,我们可以看到该请求带回了俩个Cookie

  • a=xxx
  • b=yyy

为啥有多个?

因为服务器的"记忆能力"实在是太差,一个Cookie可能不够用。所以,服务器有时会在响应头里添加多个 Set-Cookie,存储多个"key=value"。但浏览器这边发送时不需要用多个 Cookie 字段,只要在一行里用";"隔开就行。

HTTP请求头:带上身份证

那么怎么解决每次手动加Cookie问题?这里就用到了HTTP的请求头Cookie,在后续的每次请求,浏览器都会将你接收的Cookie放到HTTP的请求头中,一起带到服务端。到现在,我们很清楚的知道Cookie是如何在浏览器与客户端流转了。

Cookie的过期时间

在我们的身份证上,都会带有过期时间,而对于Cookie而言,也会有他的过期时间。就像是食品的"保鲜期",一旦超过这个期限浏览器就认为是 Cookie 失效,在存储里删除,也不会发送给服务器。

Cookie的有效期可以使用两个属性字段来设置

  • "Expires" 俗称"过期时间",用的是绝对时间点,可以理解为"截止日期"(deadline)。
  • "Max-Age" 用的是相对时间,单位是,浏览器用收到报文的时间点再加上 Max-Age,就可以得到失效的绝对时间。

Expires 和 Max-Age 可以同时出现,两者的失效时间可以一致,也可以不一致,但浏览器会优先采用 Max-Age 计算失效期。

比如举个个例子里,Expires 标记的过期时间是"GMT 2023 年 11 月 1 号 零 点 过 20 秒",而 Max-Age 则只有 10 秒,如果现在是 11 月 1 号零点,那么 Cookie 的实际有效期就是"11 月 1 号零点过 10 秒"。

当然,如果你没有设置Cookie的过期时间,那么他的生命周期就是浏览器关闭就没了。

中华人居民身份证在全中国都有效,但是一旦出了国外,就不一定有效了。而对于Cookie也同理,我们不能用A服务器的Cookie去访问B服务器,这是无效的。我们需要设置一下 Cookie 的作用域,让浏览器只发送给特定的服务器和 URL,避免被其他网站盗用。

Cookie的作用于可以使用下面两个属性字段设置

  • "Domain":指定了Cookie所属的域名
  • "Path":指定了 Cookie 所属路径

浏览器在发送 Cookie 前会从 URI 中提取出 host 和 path 部分,对比 Cookie 的属性。如果不满足条件,就不会在请求头里发送 Cookie。

使用这两个属性可以为不同的域名和路径分别设置各自的 Cookie,比如"/avv-1"用一个 Cookie,"/avv-2"再用另外一个 Cookie,两者互不干扰。不过现实中为了省事,通常 Path 就用一个"/"或者直接省略,表示域名下的任意路径都允许使用 Cookie,让服务器自己去挑。

还有一点就是:

从上面那张图中我们也能够看到,Cookie 是由浏览器负责存储的,而不是操作系统。所以,它是"浏览器绑定"的,只能在本浏览器内生效。如果你换个浏览器或者换台电脑,新的浏览器里没有服务器对应的 Cookie,"健忘"的服务器也就认不出来了,只能再走一遍 Set-Cookie 流程了。

Cookie的安全性

写过前端的同学一定知道,在 JS 脚本里可以用 document.cookie 来读写 Cookie 数据,这就带来了安全隐患,有可能会导致"跨站脚本"(XSS)攻击窃取数据。

属性"HttpOnly"会告诉浏览器,此 Cookie 只能通过浏览器 HTTP 协议传输,禁止其他方式访问,浏览器的 JS 引擎就会禁用 document.cookie 等一切相关的 API,脚本攻击也就无从谈起了。

另一个属性"SameSite"可以防范"跨站请求伪造"(XSRF)攻击,设置成"SameSite=Strict"可以严格限定 Cookie 不能随着跳转链接跨站发送,而"SameSite=Lax"则略宽松一点,允许 GET/HEAD 等安全方法,但禁止 POST 跨站发送。

还有一个属性叫"Secure",表示这个 Cookie 仅能用 HTTPS 协议加密传输,明文的 HTTP 协议会禁止发送。但 Cookie 本身不是加密的,浏览器里还是以明文的形式存在。

Chrome 开发者工具是查看 Cookie 的有力工具,在"Network-Cookies"里可以看到单个页面 Cookie 的各种属性,另一个"Application"面板里则能够方便地看到全站的所有 Cookie。

Cookie常用来做什么

  • 广告跟踪
  • 自动登录

不过,我在访问一些网站的时候,他会问我需不需要接收该网站所有Cookie以便获取"更好"的服务。

好了,到此为止Cookie我们已经讲完了。下面以简单的登录方案来讲解Session。

登录方案:Cookie-Session

我们可以想一想,我们在使用任意一个网站的时候,是怎么一个流程。我们进去第一时间要么直接要求我们登录,要么等我们进去想去进行一些操作例如购物、评论这些操作的时候,就会被拉去强制登录。

那么他前端可能只存储了一个id字段,来查看你是否已经登录。我们来看第一种登录方案。

  • 用户使用浏览器访问登录页面,输入账号密码。
  • 服务端收到请求,进行鉴权。
  • 成功后,将登录用户信息存储在Session当中,并且生成一个名为JSESSIONID 的 Cookie返回
  • 浏览器将JSEESIONID 保存在浏览器当中
  • 此后该浏览器再去请求业务接口,JSESSIONID 随 cookie 带上
  • 服务端查 JSESSIONID 校验 session
  • 成功后正常做业务处理,返回结果

这是后端使用Tomcat的Session所生成的JSESSIONID

伪代码:

typescript 复制代码
java
复制代码
public LoginUserVO userLogin(String userAccount, String userPassword, HttpServletRequest request) {
	// 1. 校验
	// 2. 加密
	// 3. 记录用户的登录态
	request.getSession().setAttribute(USER_LOGIN_STATE, user);
	// return 
}

Session 的存储方式

Session被称为服务端存储数据技术,他依赖的仅仅是返回给前端的Cookie:SessionId,因此在服务端需要存储一些额外的数据,例如用户的个人信息。

存储的方式有几种:

  • Redis:内存型数据库。以 key-value 的形式存,正合 sessionId-sessionData 的场景;且访问快。
  • 内存:直接放到变量里。一旦服务重启数据就会消失(默认)
  • 数据库:普通数据库。性能不高。

Session 的过期和销毁(退出登录)

在不同的环境Session的设置是不一样的。在SpringBoot 2.7中是这样配置的。单位是秒(s)

yaml 复制代码
yml
复制代码
spring:
  session:
    # 30 天过期
    timeout: 2592000

伪代码:

typescript 复制代码
java
复制代码
public boolean userLogout(HttpServletRequest request) {
	if (request.getSession().getAttribute(USER_LOGIN_STATE) == null) {
		throw new BusinessException(ErrorCode.OPERATION_ERROR, "未登录");
	}
	// 移除登录态
	request.getSession().removeAttribute(USER_LOGIN_STATE);
	return true;
}

分布式下Session不共享问题

在分布式下,使用内存实现的Session则限制十分大,用户服务可能部署的不止一台机器上。所以用户请求过来会走一次负载均衡,不一定打到哪台机器上。那一旦用户后续接口请求到的机器和他登录请求的机器不一致,或者登录请求的机器宕机了,Session就会失效。

解决方案:

一是从存储角度,把 session 集中存储。如果我们用独立的 Redis 或普通数据库,就可以把 session 都存到一个库里。

二是从分布角度,让相同 IP 的请求在负载均衡时都打到同一台机器上。

但通常还是采用第一种方式,因为第二种相当于阉割了负载均衡模式,且仍没有解决用户请求的机器宕机的问题。

一些成熟的方案:

  • Tomcat配置Session共享(Session复制)

  • 引入Spring Session (第三方存储如存储在Redis)

相关推荐
y先森1 小时前
CSS3中的伸缩盒模型(弹性盒子、弹性布局)之伸缩容器、伸缩项目、主轴方向、主轴换行方式、复合属性flex-flow
前端·css·css3
前端Hardy1 小时前
纯HTML&CSS实现3D旋转地球
前端·javascript·css·3d·html
susu10830189112 小时前
vue3中父div设置display flex,2个子div重叠
前端·javascript·vue.js
IT女孩儿3 小时前
CSS查缺补漏(补充上一条)
前端·css
吃杠碰小鸡4 小时前
commitlint校验git提交信息
前端
虾球xz4 小时前
游戏引擎学习第20天
前端·学习·游戏引擎
我爱李星璇4 小时前
HTML常用表格与标签
前端·html
疯狂的沙粒4 小时前
如何在Vue项目中应用TypeScript?应该注意那些点?
前端·vue.js·typescript
小镇程序员5 小时前
vue2 src_Todolist全局总线事件版本
前端·javascript·vue.js
野槐5 小时前
前端图像处理(一)
前端