概念

Cookie是服务器发送到用户浏览器并保存在浏览器上的一块数据,它会在浏览器下一次发起请求时被携带并发送到服务器上。——MDN

来做个实验

服务端代码

const http = require('http')
const path = require('path')
const url = require('url')
const fs = require('fs')

http.createServer((req, res) => {
  let {
    pathname,
    search
  } = url.parse(req.url)
  let cookie = req.headers.cookie
  console.log(JSON.stringify(cookie))
  if (pathname === '/') {
    const file = path.resolve(__dirname, './static/index.html')
    fs.readFile(file, (err, data) => {
      res.writeHead(200, {
        'Content-Type': 'text/html',
        'Set-Cookie': ['author=nbb3210', 'description=genius']
      })
      res.write(data.toString())
      res.end()
    })
  } else {
    res.end(pathname)
  }
}).listen(3210)

./static/index.html内容如下

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <link rel="icon" href="data:;base64,=">
  <title>Cookie</title>
</head>

<body>
  Cookie
</body>

</html>

这里推荐一款chrome插件,EditThisCookie,方便进行cookie的管理

启动服务端,当我们第一次访问http://localhost:3210/时,服务端控制它打印出undefined;当我们刷新页面,再次访问http://localhost:3210/时,控制台打印出"author=nbb3210; description=genius"

"author=nbb3210; description=genius"是由服务器通过设置响应头

res.writeHead(200, {
  'Set-Cookie': ['author=nbb3210', 'description=genius']
})

// 响应头
HTTP/1.1 200 OK
Content-Type: text/html
Set-Cookie: author=nbb3210
Set-Cookie: description=genius
Date: Wed, 13 Dec 2017 12:35:39 GMT
Connection: keep-alive
Transfer-Encoding: chunked

发送给浏览器,下次发送请求时,浏览器便会在请求头上带上这段信息。

// 浏览器第一次访问服务端时发送的请求头
GET / HTTP/1.1
Host: localhost:3210
Connection: keep-alive
Cache-Control: max-age=0
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.94 Safari/537.36
Upgrade-Insecure-Requests: 1
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8
// 浏览器第二次访问服务端时发送的请求头,包含有了cookie信息
GET / HTTP/1.1
Host: localhost:3210
Connection: keep-alive
Cache-Control: max-age=0
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.94 Safari/537.36
Upgrade-Insecure-Requests: 1
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8
Cookie: author=nbb3210; description=genius

这样,服务端就可以分辨出不同浏览器端发来的请求,记录用户的状态,同样的请求,因为cookie的不同会有不同的响应。总而言之,借助cookie,服务端能区分出“你是谁?”,从而记起“你之前干了啥”

cookie的属性

Domain, Path

服务端设置cookie时,可以设置domainpath,从而确定该cookie的作用域。

例如,如果设置了Domain=mozilla.org,则浏览器访问其子域名(如developer.mozilla.org)也会发送cookie。如果不设置domain,默认为域名A(网站A)向浏览器发送的cookie,只有在浏览器再次向域名A(网站A)发送请求时才会被带上。

这里的path匹配scheme:[//[user[:password]@]host[:port]][/path][?query][#fragment]中的path。例如,在上面的实验中,修改代码

'Set-Cookie': ['author=nbb3210; Path=/abc', 'description=genius']

用插件清除cookie,分别访问http://localhost:3210/http://localhost:3210/abdhttp://localhost:3210/abc,http://localhost:3210/,服务端控制台分别打印出undefined"description=genius""author=nbb3210; description=genius""description=genius",因为只有第三次的url与cookie的path设置匹配。

HttpOnly

cookie的值可以被修改,document.cookie="author=ahhh",再次刷新页面,重新发送请求,服务端控制台就会打印"description=genius; author=ahhh"

如果把自己的cookie修改成别人的cookie,那么就可以伪装成别人,向服务端发送请求,这里的话。。。

如果cookie被设置了HttpOnly,那么再发送请求时,cookie会被带上,而浏览器端JavaScript无法修改该cookie。

例如,在上面的实验中,修改代码

'Set-Cookie': ['author=nbb3210; HttpOnly', 'description=genius']

用插件清除cookie,两次访问http://localhost:3210/,服务端控制台分别打印出undefined"author=nbb3210; description=genius",其后,打开浏览器控制台,

> document.cookie
< "description=genius"
// 而 author=nbb3210 对于JavaScript来说是不可见的了

再次设置cookie

> document.cookie = "author=ahhh"
< 控制台打印 "author=ahhh"

并刷新页面,重新访问http://localhost:3210/,服务端控制台仍打印"author=nbb3210; description=genius"

Secure

只有在安全连接的https时,cookie才会被发送

Expires, Max-Age

前面的属性确认了cookie在什么时候会被发送,ExpiresMax-Age确定了cookie在什么时间范围内有效。如果不设置这两个属性,cookie在浏览器关闭时就会被删除,所以在关闭浏览器后,再次访问http://localhost:3210/,服务端控制台会打印undefined

Max-Age设置了cookie从被创建到消失持续的时间(s),如修改代码

'Set-Cookie': ['author=nbb3210; Max-Age=30', 'description=genius']

用插件清除cookie,两次访问http://localhost:3210/,服务端控制台分别打印出undefined"author=nbb3210; description=genius",超过30s后再次访问http://localhost:3210/,服务端控制台打印出"description=genius",超过30s后,author=nbb3210失效了。

Expires指定了过期时间GMT,如果修改代码

let date = new Date()
date.setTime(date.getTime() + 30 * 1000)
...
'Set-Cookie': [`author=nbb3210; Expires=${date.toUTCString()}`, 'description=genius']

其作用与设置Max-Age为30s的效果一样


nbb3210
436 声望31 粉丝

优雅地使用JavaScript解决问题