1 Cookie和Session
1.1 http无状态性
- http协议是无状态的,所谓无状态就是指
每次请求都是独立的
,它的执行情况和结果不会受前后请求响应的直接影响,对于服务器来说就是每次请求都是新的。状态是啥?状态可以理解为客户端和服务器在某次会话中产生的数据。无状态也就是会话中的数据并没有保存,但这些数据恰恰是我们需要保存的,所以Cookie就很有必要了,也就是客户端需要Cookie对这些状态进行保存。
1.2 Cookie
🍑 Cookie是啥?
Cookie意为“甜饼”,“饼干”,“曲奇饼”。是w3c组织提出的,是存储在用户浏览器中的不超过4kb的字符串
,它由一个名称(Name)、一个值(Value)和其它几个用于控制Cookie有效期、安全性、使用范围的可选属性组成,cookie是保存在客户端浏览器中的。一个浏览器能创建的 Cookie 数量最多为 300 个,并且每个不能超过 4KB,每个 Web 站点能设置的 Cookie 总数不能超过 20 个
。
-
name:键,必需的,cookie的名称,用于指定Cookie的键
-
value:值,必需的,cookie的值
-
expires:cookie过期时间,采用UTC或GMT格式,通过new date().toUTCSTring()或new date().toGMTString()获取到的时间,弱国不设置或null,cookie只在当前会话session有效,浏览器窗口一旦关闭,当前Session结束,该cookie就会被删除。
-
damain:Cookie生效的域名,所有以这个域名为结尾都可以访问该cookie
-
path:用来指定路径,必须是绝对路径如(/、/mydir),如果未指定,默认为请求该cookie的网页路径
-
secure:默认false,该cookie是否仅被使用安全协议传输,安全协议如https、ssl等,在网络上传输数据之前先将数据加密。
-
maxAge:用来是指定cookie有效期,比如606024*365等,即一年3156000s,正常下max-age优先级高于expires。
-
HttpOnly:默认false,能被JavaScript读取,即document.cookie不会返回这个cookie的值,只用于向服务器发送,主要是为了防止XSS攻击Cookie。(不是绝对,底层抓包可以获取到也可以被覆盖)
-
SameSite:cookie 在跨域时是否应该被发送。它有三个值:
Strict
(跨域请求严禁携带本站cookie)、Lax
(默认值。可通过顶级导航的方式并使用 GET 请求发时可以携带(目前我没有通过 demo 实现该效果,或者我们可以将其无限接近于 Strict)。在 Chrome 80 版本之后,Cookie 的 SameSite默认值 由原来的 None 改为了 Lax。);None
(会携带 cookie。但前提是 Secure 设置为 true,即只能在 HTTPS 协议下使用(之前的标准没有这个要求))。
注意地,Cookie具有不可跨域名性
,根据Cookie规范,浏览器访问google只会携带google地Cookie,而不会携带baidu的Cookie。浏览器判断一个网站是否能操作另一个网站Cookie的依据是域名。
🍑 Cookie的工作原理 cookie是服务器发送到用户浏览器并保存在本地的一小块数据,它会在浏览器下次向同一服务器再发起请求时被携带并发送到服务器。通常,它用于告知服务端两个请求是否来自同一浏览器,如保持用户的登录状态。
🍑 Nodejs实现Cookie
// app.js
const cookieParser = require('cookie-parser');
// 加自定义密钥
app.use(cookieParser("miyao"));
app.get('/api/setcookie', (req, res) => {
res.cookie("username", "swk", {
maxAge: 24 * 60 * 60 * 1000,
signed: true // 加密(签名)
});
res.send({
msg: "设置cookie成功!"
});
})
// 只要调用这个api,前端就可以通过document.cookie获取token
1.3 Session
🍑 Session是啥?
是另一种记录客户状态的机制,相较于Cookie,Session是保存在服务器上的
,客户端第一次请求服务器的时候,服务器生成一份session保存在服务端,将该数据的id以cookie的形式传递给客户端;以后的每次请求浏览器都会自动的携带cookie来访问服务器。
🍑 Session的常用属性:
- name:设置cookie中,保存session的字段名称,默认为connect.id
- store:session的存储方式,默认存放在内存中
- secret:通过设置的 secret 字符串,来计算 hash 值并放在 cookie 中,使产生的 signedCookie 防篡改。
- cookie:设置存放 session id 的 cookie 的相关选项,默认为 (default: { path: '/’, httpOnly: true, secure: false, maxAge: null })
- genid:产生一个新的 session_id 时,所使用的函数, 默认使用 uid2 这个 npm 包。
- rolling:每个请求都重新设置一个 cookie,默认为 false。
- resave:即使 session 没有被修改,也保存 session 值,默认为 true。
🍑 保存sessionID的方式
- 内存上或数据库
- 采用cookie保存sessionId
- URL重写,把session_id附加在URL路径的后面(一种作为URL路径的附加信息,另一种是作为查询字符串附加在URL后面)
- 表单隐藏字段,就是服务器会自动修改表单,添加一个隐藏字段,提交的时候可以把sessionId传递回服务器。
1.4 session与cookie的区别?
🍑 作用范围不同?
- Cookie保存在客户端(浏览器),容易遭到不法获取
- Session保存在服务器端,存储在服务端,安全性高
🍑 存储方式的不同?
- Cookie只能保存ASCII
- Session可以存任意数据类型,比如下面的session认证存了一个用户名信息进去
🍑 有效期不同?
- Cookie可以长时间保持,如默认登录
- Session一般失效时间较短,如客户端关闭、session超时都会失效。
🍑 存储大小不同? 单个Cookie保存数据不能超过4k,Session可存储数据远高于Cookie。
2 Session认证机制
在nodejs的启动文件app.js,引入session配置中间件,对session的一些基本属性进行配置,就可以进行相关session的存取操作了。
// 1.导入express-session中间件
const session = require('express-session')
// 2.配置Session中间件
app.use(session({
secret: 'keyboard cat', // secret属性的值可以为任意字符串
resave: false, // 固定写法
saveUninitialized: true // 固定写法
}))
只要在app.js中引入以上的中间件,并配置好session,请求req对象会自动带上以下的一些session默认信息,每次请求的时候req对象都会重新刷一次sessionId
,用来唯一。在session对象中我们可以存一些信息进去,为了判断是否是同一用户在访问当前会话。
sessionStore: MemoryStore {
_events: [Object: null prototype] {
disconnect: [Function: ondisconnect],
connect: [Function: onconnect]
},
_eventsCount: 2,
_maxListeners: undefined,
sessions: [Object: null prototype] {
'I7kPSrw_MaHnZrI1N2gPe9-u2EfYkHXD': '{"cookie":{"originalMaxAge":null,"expires":null,"httpOnly":true,"path":"/"},"user":"gy"}'
},
generate: [Function (anonymous)],
[Symbol(kCapture)]: false
},
sessionID: 'rn_4W1sIJ9U8f9aL-XVKBTPnbCGRmdGy',
session: Session {
cookie: { path: '/', _expires: null, originalMaxAge: null, httpOnly: true }
},
session是服务器在和客户端建立连接时添加客户端连接标志,当客户端第一次请求时服务器会检查是否携带session,如果没有,nodejs会随机产生一个1024比特长的字符串(sessionID),然后存到Cookie里面(connect.sid)。下次请求,服务器就判断请求是否带上Cookie。
- connect.sid格式:
's:'+sessionID+'.' + sessionid.sha256(secret).base64()
- Cookie格式:
connect.sid='s%3A'+sessionID+'.' + sessionid.sha256(secret).base64()
🍑 服务器解密Cookie的connect.sid获取sessionID与服务器中生成的sessionID进行对比验证
// 服务器解密方式
const connectSid = req.headers.cookie;
const session = connectSid.split('=')[1];
const sessionId = session.splice(4,session.indexOf(.));
// sessionId与req对象中的sessionID(注意这个并没有在客户端看到)比较
🍑 如果浏览器中禁止了Cookie,如何保障整个机制的正常运转?
- 每次请求中都携带一个SessionID的参数,也可以Post的方式提交,也可以在请求地址后面拼接XXXX?SessionID=.....
- Token机制