五一假期第3天,2023年5月1日继续打卡分享自己的前端笔记,今天就分享我关于Cookie, Session,Token和JWT的相关笔记和理解吧。本文为原创,未经同意请勿转载
由于篇幅有点长,所以笔者将我关于这部分的笔记分为四个篇章(文章开头后面附录上下篇链接),避免读者的阅读疲倦感,同时也方便大家的阅读啦。如果下面笔记中存在错误,欢迎大家及时指正,学习与讨论。
首先,先介绍Cookie/Session/Token/JWT它们出现的一个背景(更加准确的描述其实应该是Cookie出现的背景),也就是HTTP的无状态属性。
众所周知,我们访问网页一般都是使用HTTP/HTTPS协议,然而HTTP是一种不保存状态,即无状态协议。那什么是无状态呢?也就是这一次请求和上一次请求是没有任何关联的。这种无状态的好处就是可以快速响应。但是如果服务端需要辨别请求时哪个客户端发送,或者需要关联两个请求/响应时,就需要获取请求或者响应的状态,因此这种无状态便无法满足我们的需求了。因此,为了使某个域名下的所有网页能够共享某些状态数据,引申出了Cookie和Session。
总结而言
由于HTTP是无状态协议,不保存通信状态,无法标识客服端,所以Cookie、Session和Token都是用来解决这个问题的(做持久化处理的),目的就是让客户端,也就是浏览器和服务端互相认识。
Cookie、Session、Token和JWT都是用于标识客户端的,而且从发展上来看可以看成网络安全的一个发展吧。那它们之间有什么区别,下面笔者我将详细对每一个部分展开说明。
首先,Cookie由服务器创建的,只保存在浏览器中的小型文本文件,格式为key=value,一般包含用户信息,登录及访问权限等。
(1)由服务器生成并存储在客户端的
(2)以普通文本方式存储键值对数据,无法执行代码
(3)不可跨域:每个Cookie会绑定单一的域名,无法在别的域名下使用,一级域名和二级域名之间是允许共享使用的(靠的是 domain)。同一个域名下的不同页面之间可以共享Cookie,同域名不同端口也是允许共享使用的。
(4)有过期时限:Cookie会在过期时间到达之后自动删除
(5)文件体积大小,数量有限制:一般在4KB左右,数量在50个左右
(6)自动发送
(7)只能存储String 类型的对象
Cookie
字段中。Set-Cookie
字段中。Cookie集合中的每个Cookie都拥有下面这些重要的属性,而且每个Cookie的这些属性都是独立分开的,各自控制各自的Cookie。
数据类型:在Cookie中,设置键值对的键名name和键值value都必须是字符串类型,如果是Unicode
字符,需要为字符编码;如果值为二进制数据,则需要使用 BASE64 编码。
domain:指定 Cookie 所属域名,默认是当前域名
path: 指定 Cookie 在哪个路径(路由)下生效,默认是 ‘/’
maxAge: Cookie 失效的时间,单位秒,默认为 -1。如果为正数,则该 Cookie 在 maxAge 秒后失效。如果为负数,该 Cookie 为临时 Cookie ,关闭浏览器即失效,浏览器也不会以任何形式保存该 Cookie 。如果为 0,表示删除该 Cookie 。
secure :设置Cookie是否使用安全协议传输。安全协议有 HTTPS,SSL等,在网络上传输数据之前先将数据加密。默认为false。当 secure 值为 true 时,Cookie 在 HTTP 中是无效,在 HTTPS 中才有效。
HttpOnly:如果给某个 Cookie 设置了HttpOnly属性,则无法通过 JS 脚本 读取到该Cookie 的信息,但还是能通过 Application 中手动修改 Cookie,所以只是在一定程度上可以防止 XSS 攻击,不是绝对的安全。
SameSite:设置Cookie在跨域请求的时候不能被发送。
认证流程简述:在用户第一次访问网站服务器时,服务器会通过响应头Set-Cookie字段发给浏览器,浏览器接收后会把Cookie信息以键值对的形式保存到浏览器本地某个txt文件中。在后面访问的时候,浏览器就都会将保存的Cookie发送给服务器,以此让服务器识别身份(让服务器知道这个请求来自于哪个客户端)。
一个例子:
Set-Cookie: "name=value;domain=.domain.com;path=/;expires=Mon, 01 May 2023 14:00:00 GMT;HttpOnly;secure"
注意:
这里需要注意的一点是客户端第一次向浏览器请求时,携带的Cookie是空的(也就是相当于没有Cookie),响应页面也没有给Cookie设置,所以Cookie实际上就是在服务器中设置和生成的,并返回给客户端保存
认证流程步骤叙述:发布——》检索——》验证
1、发布:当用户想要访问和使用网站里经过认证的资源时,Web服务器会检测与核查用户是否提供了cookie,对用户的请求进行审核与验证。
2、检索:用户提出了访问请求,客户端浏览器会检索与用户请求相匹配的Cookie。
3、验证:Web服务器会验证是否有访问令牌,如果有访问令牌的话,则同意用户的请求,如果没有这个令牌,则会拒绝用户的访问和登录请求。
优点总结
:极高的扩展性和可用性,便于使用、实现和管理,占用内存少,具有可持久性,透明性。优点展开
:
注:同域
):Cookie 可以在同一个网站的不同页面之间传递数据,比如购物车中的商品信息缺点总结
:虽然Cookie简单便于管理,但是Cookie存在隐私隐患,安全性问题,禁用限制问题和大小及访问限制等问题。缺点展开
:
主要指跨域
):Cookie 是不可跨域的,要实现跨域需要使用Token等。此外,Cookie与浏览器相关,不能互相访问。常用场景总结
:(1)登录状态及用户信息的管理;(2)跟踪用户行为,统计分析,广告定位;(3)记住用户偏好设置,定制页面;(4)创建购物车;(5)缓存数据,用于搜索历史和浏览记录;(6)跨页面数据传递,实现数据共享与同步 …展开
:
经典场景之购物车
:Cookie购物车一般指网站上的电子商务功能,在用户浏览产品后,将所选商品信息保存到浏览器的Cookie中,并在结账时读取Cookie中的信息以便显示或更新所选商品内容和数量。通常情况下,Cookie购物车还会在用户离开网站后保存所选商品信息,以便用户下次访问同一网站时继续购物,或在用户登录到网站时提供更个性化的服务。然而,由于安全原因,有些用户可能不愿意让浏览器储存他们的个人信息(例如地址和支付方式),因此一些商家采用了其他方法来保存购物车内容。需要注意的是,使用 Cookie 存储数据时应当注意数据的安全性和敏感性。敏感信息,例如用户的密码、支付信息等,不应当存储在Cookie中,而应当使用更加安全的方式面临的挑战当然也包括Cookie的缺点,这里就不再赘述,这里主要讲一下禁用限制问题的解决方法:
可以将 Cookie 信息放到 URL的 params 中或者请求的 body 中,但一般的解决方案是放在 url 的 params 中,通过重写 URL的方式传递。
由于Cookie可以被人为的禁止,必须有其他机制以便在Cookie被禁止时仍然能够把session id传递回服务器。经常被使用的一种技术叫做URL重写,就是把Session id直接附加在URL路径的后面,附加方式也有两种:
http://…/xxx;jsessionid=ByOK3vjFD75aPnrF7C2HmdnV6QZcEbzWoWiBYEnLerjQ99zWpBng!-145788764
http://…/xxx?jsessionid=ByOK3vjFD75aPnrF7C2HmdnV6QZcEbzWoWiBYEnLerjQ99zWpBng!-145788764
前面我们讲了Cookie是可以被人为禁止的,那我们确保在执行我们的Cookie是可用的?通过判断是否禁用来选择不同的解决方案。
要检查浏览器是否禁用Cookie,可以使用JavaScript中的navigator.cookieEnabled属性来判断。这个属性返回布尔值,指示浏览器是否允许Cookie,如果值为true,则当前Cookie是启用的;反之则可能是禁用的。
if (navigator.cookieEnabled) {
alert("Cookies are enabled");
} else {
alert("Cookies are disabled");
}
⚠ 这里需要注意有2点
:
(1)这种方法并不能完全确定用户的浏览器是否完全禁用了Cookie。因为有一些非持久化的cookie仍然可以在当前浏览器会话生命周期内使用,即使用户明确禁用了所有cookie。
(2)返回false时并不意味着用户明确禁用了Cookie。 还可能存在其他因素干扰Cookie,例如隐私设置或运行安全软件等。
因此,在实际开发中,使用navigator.cookieEnabled
属性来判断是否启用Cookie并不可靠。更好的方法是尝试写入一个持久化的Cookie,如果成功则表示用户的浏览器支持Cookie。否则,建议提醒用户在其浏览器设置中启用Cookie功能。
在Web中,客户端对Cookie的操作主要包括读取和设置(修改更新和删除等)。
(1)读取Cookie
通过document.cookie
属性可以访问当前页面所有存在的Cookie信息,比如:
var cookies = document.cookie;
document.cookie
返回值为一个字符串,该字符串有一系列键/值对组成,比如:
"fryname1=value1; fryname2=value2"
其中每个Cookie用分号(;)隔开,而每个Cookie的键和值用等号(=)隔开。而且返回的每个Cookie值并不包含键/值以外的其他Cookie属性。
如果要获取某个特定Cookie的值,可以采用字符串截取或者正则表达式进行匹配。例如,获取名为fryname2
的Cookie值:
var cookies = document.cookie;
var cookieValue = null;
if (cookies && cookies !== '') {
var cookieArray = cookies.split(';');
for (var i = 0; i < cookieArray.length; i++) {
var cookie = cookieArray[i].trim();
if (cookie.substring(0, 8) === 'fryname2') {
cookieValue = cookie.substring(9, cookie.length);
break;
}
}
}
console.log(cookieValue);
(2)设置Cookie相关属性
通过JavaScript可以向客户端设置Cookie值。可以使用document.cookie
来设置Cookie的相关属性。
document.cookie = "key=value;expires=date;path=path;domain=domain;secure";
由于Cookie的键/值中的值是不允许包含分号、逗号和空白符,因此,在存储前一般可以采用 encodeURIComponent() 函数对值进行编码。相应的,读取cookie值的时候要用 decodeURIComponent() 函数解码。
(3)更新cookie
要更新一个 Cookie,可以直接覆盖它。例如,若有一个名为 oldCookie 的Cookie,并想将它的值设为 newCookieValue,则可以:
document.cookie = 'oldCookie=newCookieValue';
这样子便可直接覆盖旧Cookie 的所有属性,包括它的到期时间和路径。但如果只是更新某个值,则分别指定新的值便可。
(4)删除cookie
要删除一个Cookie,可以通过设置它的过期时间来实现。
可以将过期时间设置为任何早于当前时间的日期,这样子就可以立即将 Cookie 删除。下面以为删除fryname2
对应的Cookie为例子:
document.cookie = 'fryname2=; expires=' + new Date(0).toUTCString();
将 expires
属性设置为 0(也可以设置为日期对象,但是要比当前时间早),表示该 Cookie 已经过期了。当然,除了设置 expires
属性,我们也可以通过设置max-age
属性为 0 来实现(现在更多用的是max-age属性,max-age属性的精确度更高,更加稳定)。注意,我们还有一个空值 fryname2=
,这是把 value 设置为空的方法。
补充:
expires
和max-age
都用来设置Cookie的有效时间,expires
是指定一个日期与客户端本地的时间进行比较,max-age
类似倒计时,Cookie在设置后的若干毫秒内有效。本地的时间是可以被修改的,而同一台服务器的时间总是相同的,因此max-age
比 expires
更加稳定可靠。
此外,还可以进一步通过设置path
和domain
属性来选择删除Cookie,因为它们允许选择性地删除指定域名下的Cookie。
// 要删除的 cookie 名称
var cookieName = "ryCookie";
// 设置 cookie 过期时间为过去的时间,让浏览器自动删除该 cookie
document.cookie = cookieName + "=;expires=Mon, 01 May 2023 14:00:00 UTC;path=/;domain=example.com";
在上面的代码中,将 path
设置为 “/”,这将使得浏览器删除主域名下所有具有相同名称的Cookie。如果只想删除某个子目录下的Cookie,则需要将 path
设置为该子目录路径。同样的,需要注意将 domain
替换为特定的域名。
在Web中,服务端对Cookie的操作主要包括创建和读取。
(1)创建Cookie
当服务器向客户端发送HTTP响应时,在响应头中可以包含Set-Cookie字段来创建一个新的Cookie。下面是一个用 Node.js
代码创建一个名称为“ryCookie”
的Cookie,并将其值设为“hello world”
和持续时间为10秒钟的示例:
var http = require('http');
http.createServer(function(req, res) {
res.setHeader('status', '200 OK');
res.setHeader('Set-Cookie', 'ryCookie=hello%20world;Max-Age=10'); // 这里创建
res.write('Hello World');
res.end();
}).listen(8080);
console.log('running localhost:8080')
如果想设置多个cookie,在设置时可以将多个cookie存放在数组中,例如:
// 存储name为username,value为xiaobaiRy的cookie,过期时间为一小时后
const cookie1 = 'username=xiaobaiRy; Max-Age=3600';
// 存储name为user_id,value为123456的cookie
const cookie2 = 'user_id=123456';
res.setHeader('Set-Cookie', [cookie1, cookie2]);
(2)读取Cookie
当客户端向服务器发出HTTP请求时,其中可能包含它之前由该服务器设置的Cookies。在Node.js中,可以通过request对象的headers属性访问所有HTTP头信息,所以读取Cookies信息可以:
const cookies = request.headers.cookie;
通过上面的知识,我们也对背景和Cookie有了一个大概的了解,所以在中篇中,笔者我将主要介绍一下Session的相关要点以及Session和Cookie的区别。
笔记链接:【前端知识】Cookie, Session,Token和JWT的发展及区别(二)
码字不易,可能当中存在某些字体错误(笔者我没有发现),如果有错误,欢迎大家指正。