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)

相关推荐
NiNg_1_2341 分钟前
Echarts连接数据库,实时绘制图表详解
前端·数据库·echarts
如若12339 分钟前
对文件内的文件名生成目录,方便查阅
java·前端·python
滚雪球~1 小时前
npm error code ETIMEDOUT
前端·npm·node.js
沙漏无语1 小时前
npm : 无法加载文件 D:\Nodejs\node_global\npm.ps1,因为在此系统上禁止运行脚本
前端·npm·node.js
supermapsupport1 小时前
iClient3D for Cesium在Vue中快速实现场景卷帘
前端·vue.js·3d·cesium·supermap
brrdg_sefg2 小时前
WEB 漏洞 - 文件包含漏洞深度解析
前端·网络·安全
胡西风_foxww2 小时前
【es6复习笔记】rest参数(7)
前端·笔记·es6·参数·rest
m0_748254882 小时前
vue+elementui实现下拉表格多选+搜索+分页+回显+全选2.0
前端·vue.js·elementui
星就前端叭2 小时前
【开源】一款基于Vue3 + WebRTC + Node + SRS + FFmpeg搭建的直播间项目
前端·后端·开源·webrtc
m0_748234522 小时前
前端Vue3字体优化三部曲(webFont、font-spider、spa-font-spider-webpack-plugin)
前端·webpack·node.js