前言
前面我们说到了HTTP是一个无状态的协议,服务器向客户端发送被请求的文件,而不存储任何关于客户端的状态信息,然而一个Web站点通常希望可以识别用户的身份,于是cookie应运而生
1.cookie
1.1概述
上回提到HTTP属于无状态协议,这并非没有原因,这种设计简化了服务器的结构,允许工程师去开发可以同时处理数千个TCP连接的高性能Web服务器,然而一个站点通常希望能够识别用户的身份,因此HTTP中定义了cookie,它允许浏览器和服务器跟踪并记录用户的信息
1.2cookie基本结构以及实现原理
cookie技术有4个组件:
(1)在HTTP响应报文中的一个cookie首部行
(2)在HTTP请求报文中的一个cookie首部行
(3)在用户端系统中保留一个cookie文件,并由用户的浏览器进行管理
(4)位于Web站点的一个后端数据库,cookie存储在其中
实现原理
我们以下图为例:
我们使用字母A表示服务器,字母B表示客户机
假设客户第一次使用B访问服务器A,该服务器将产生一个唯一识别码,并以此作为索引,在自身的cookie数据库中产生一个文件,用于记录用户信息。接下来,服务器向客户机B返回一个HTTP响应报文,里面包含“Set-cookie:”,Set-cookie首部含有识别码
此处我们设定Set-cookie:1601
当主机B浏览器收到HTTP响应报文时,它会把报文中包含服务器A的主机名和“Set-cookie"首部中的识别码部分,添加至它自身管理cookie文件的一行,并且该文件中已经保存了一个站点的网址eBay,因为主机B访问过此网址。
此后当客户机B浏览器继续访问A域名下的网站时,每请求一个Web页面,其浏览器都会查询该cookie文件并且抽取它对这个网站的识别码,放置到HTTP请求的报文当中,发往服务器A的HTTP请求报文都包含Cookie:1601
此时我们就实现了对用户信息的追踪,通过客户机每次发起HTTP请求里携带的cookie信息,服务端就可以记录该用户按照什么顺序在哪些时间访问了哪些网页
在一周之后,或者更晚,客户机B通过同一浏览器继续访问站点A时,它的浏览器发起HTTP请求时仍然会带有Cookie:1601,站点A会根据这个用户(1601)访问过的网页来推荐产品。如果客户机B的用户曾经在A站点注册过,留下了邮箱、手机号等信息,那么服务器A将可以在自身的数据库中将cookieI与该用户的邮箱、手机号等绑定在一起,这就是为什么在同一购物网站里,购买不同的商品,不用重复输入邮箱和手机号等
1.3实际应用
现代浏览器中,开始禁用第三方js设置cookie,因为cookie在提供验证用户身份信息的同时,也给用户带来了信息泄露的风险
新增属性SameSite:Strict /Lax /None;用于设定浏览器是否接受引入第三方cookie
cookie默认不支持跨域共享,也就是说不同域名下,cookie不能互通,除非它们都处于同一域名下
2.Session
2.1概述
通过cookie,浏览器和服务器可以实现状态的记录,但是cookie并非完美无缺。比如说,由于cookie的实现机制,一旦服务器向客户端发送了设置cookie的意图,除非cookie过期,否则每次请求都会发送这些cookie到服务器端,一旦设置的cookie过多,就会导致报文过大,这会造成宽带的浪费
最为关键的是,在前后端可以修改cookie的值,因此数据就容易遭受篡改和伪造
session的设立就是为了解决这一问题,session的数据只保留在服务器端,客户端无法修改,这样数据的安全性就得以保证
2.2session实现原理
服务器第一次接收到请求时,就会根据用户信息创建session对象(里面包含sessionID)
服务器发送响应请求返回sessionID,客户端浏览器接收到sessionID值之后,会将sessionID写入cookie中,同时记录sessionID属于哪一个域名
当用户之后在访问这一域名下的网站时,服务器就会获取HTTP请求中cookie里的sessionID,再查护自身数据库中是否有与sessionID一致的信息,如果没有找到说明用户没有登录或者登录失效,如果找到 Session 证明用户已经登录可执行后面操作。
详情可以参照大神文章: juejin.cn/post/684490…
2.3基于nodejs实现session
const experss = require("express");
const app = experss()
var session = require("express-session")
// 首先注册session服务,提供安全验证
app.use(session({
secret : 'password',
resave : false,
saveUninitialized : true,
}))
// 注册静态服务器
// 分享的是文件夹
app.use(experss.static('static',experss.static('files')))
// 数据解析
app.use(experss.urlencoded({ extended : false }))
app.post('/p/a', (req, res) => {
if(req.body.username != 'admin' || req.body.password !== '123456') {
return res.send({statu : 0, msg : '登录失败'})
}
// 自定义属性
req.session.user = req.body.username;
req.session.password = req.body.password;
req.session.isLogin = true;
res.send({statu : 1, msg : "登录成功"})
})
app.listen(80, () => {
console.log("http://127.0.0.1");
})