
HTTP 是一个“无状态协议”,也就是说,每次从客户端对服务器发出的请求都是独立的 — 这一次的请求无法得知上一次请求的内容与信息。
既然 HTTP 是一个无状态协议,那么服务器如何识别不同的请求是来自同一个浏览器?或者用户登录后,服务器如何在往后的请求中,识别用户其实已经通过验证(已登录)的状态? — 通过使用 Cookie 和 Session 可以解决这些问题。
Cookie 是服务器传送给浏览器的一小段数据,并请浏览器保存起来,以便往后向相同的服务器发送请求时,附上这个 Cookie 的数据。
透過 res.setHeader(‘Set-Cookie’, <cookie-name>=<coolie-value>) 設定回傳 的 Cookie 和內容:
// 以登录例子做设置,在登录后附上含有“已登录状态”的 Cookie
app.post('/login', (req, res) => {
res.setHeader('Set-Cookie', 'isLoggedIn=true')
res.redirect('/')
})app.get('/anotherPath', (req, res) => {
const cookie = req.get('Cookie')
console.log(cookie)
res.render('anotherPage')
})从下方 Terminal 的结果可以发现,不管是对 /anotherPath 这条路径,或其他相同服务器的不同路径发出请求,从该客户端向服务器发出的请求,都会附上包含 isLoggedIn 信息的 Cookie:
通过 Cookie,我们的确达到目标,让服务器可以在往后通过客户端发出的请求,识别用户及其登录状态,非常方便。然而,有趣的是,这些信息其实用户是有机会可以在浏览器中修改的,因此用户能通过串改 Cookie 上的=内容,让服务器收到不正确的信息 — 也就是说,以登录的例子来看,用户可以在串改 isLoggedIn 的值,让服务器误以为用户已经通过认证:
尽量避免将敏感信息通过 Cookie 存在客户端
我们了解到存放较敏感的信息在客户端是有安全上的疑虑,也因此我们改使用 Session 将用户相关的敏感信息存放在服务器端 — 可能在内存或数据库中 — 并创建一个相对应且独特的 ID(Session ID),在回传给客户端的 Cookie 中一并附上,未来客户端只要附上含有这个 Session ID 的 Cookie 给服务器,服务器就能匹配相对应的 Session — 也能找到需要的敏感数据了!
想像你入住了迪士尼酒店,酒店告知有关你的入房、预约餐厅和游乐设施的信息都已经存在迪士尼的系统当中了,并附上一只专属的 MagicBand,手表芯片中有系统给你的专属 ID 号码,因此这几天在酒店直接感应房门、在餐厅或游乐设施前感应机器就能通过系统辨识完成开门和报到。
你的个人信息很重要,因此迪士尼将入房、预约信息以 Session 存放在系统中,这笔数据对应了一个独特的 Session ID。而 MagicBand 就像是 Cookie,当中带有这个 Session ID,当你透过 MagicBand 在感应机器时,系统就能快速辨识你的身份,并找到存放你入房和预约数据的 Session 了!
如同前面所说的,重要的信息不建议放在客户端 — 像是写在你的 MagicBand 上,你就有机会串改游乐设施的预约时间、其他人也有机会看到你的隐私等 — 若放在迪士尼的系统中(像是服务器的概念),就显得安全许多:你唯一需要的,是那只含有独特 ID 的 MagicBand 让系统辨识罢了!
$ npm install express-session// 引入 express-session 包
const session = require('express-session')// 建立 session 的 middleware,并将在需要带入的属性放进 options 对象中
app.use(session(options))options 对象中可以带入的属性非常多,其中最重要的是 secret — secret 的值将被用来做作为制造存放在 Cookie 当中的 hashed session ID,通常是一串很长的字符串:
补充:这边提到的 hashed session ID 可以想象是把你提供的 secret 字符串通过特殊的杂凑算法(hashing algorithm)生成的一组唯一 ID
// 例子
app.use(session({
secret: 'sndkfnofnosfpowekmprwjqlierjw',
// 代表在每次与用户互动后,不会强制把 session 存储,除非 session 有变动
resave: false,
saveUninitialized: false
}))// 在 session 对象上新增 key 和 value
app.post('/login', (req, res) => {
req.session.isLoggedIn = true
res.redirect('/')
})在浏览器发出登录请求后,可以看到多了一个 Cookie,其中的价值就是 hashed Session ID — 用来让服务器辨识和寻找相对应的 Session
为了验证同一个用户通过客户端发出的不同请求都会附上含有 Session ID 的 Cookie 给服务器,且服务器可以通过 Cookie 上的 Session ID 找到相对应的 Session data,我们在 / 路径的路由中,将 Session 中 isLoggedIn 的值打印出来看看:
// 在登录后,前往 / 路径
app.get('/', (req, res) => {
// 将 session 对象打印出来
console.log(req.session.isLoggedIn)
res.redirect('index')
})从上面登录案例的实验结果可以了解到:
在没有使用 Cookie 和 Session 之前,照理来说,不同的请求都是独立的,这一次发出的请求是拿不到上一次的信息。然而通过 Session,我们得以将用户敏感信息存储在服务器端,搭配 Cookie,服务器得已通过客户端每次发出的不同请求,获取所需要的 Session ID ,找到需要使用的用户信息。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。