express学习笔记:cookie-parser中间件

Learning Card

  • http是无状态的
  • cookie和session来储存用户信息
    • cookie相当于session的暗号
  • 操作方法
    • 原生: res.setHeader('Set-Cookie', [key=value])
    • cookie-parser中间件
      • 读取: req.cookies
      • 写入: res.cookie(key, value, {attributes})
      • 签名: req.secret
      • 解析签名: server.use(cookieParser(secret))
      • 防止修改: httpOnly: true
      • 清除: res.clearCookie(key)

HTTP协议的一大特点是无状态。所谓无状态就是对于之前处理的事务没有记忆能力,我们可以理解为刚刚才登录了这个网站,一不小心关闭页面之后再打开,之前在页面上的操作就没有留下任何痕迹了,不会有现在的7天之内免登录之类的便捷操作。

这样看起来比较安全,但是会导致之后每一次使用网页都要重传之前所有的数据,非常不方便,所以就有了两个专门用来解决这一问题的技术,就是Cookie和Session。

Cookie和Session

Cookie是保存在浏览器上的数据,它用来辨识用户身份。Session则是保存在服务器上的数据,它保存了用户的信息数据。

但我们知道,保存在浏览器上的数据都是不安全的,因为用户可以自行修改上面的数据。我们随意打开一个网站,比如百度,打开开发者工具中的Applciation,查看Cookies:

image-20180812211910298

可以看到列表里有很多数据。这里我们选择BD_HOME,并在控制台修改它的值:

image-20180812212132732

此时我们再查看Application,就会发现BD_HOMEvalue已经变成了200:

image-20180812212211097

那么既然cookie这么不安全,我们为什么还使用它呢?

原理

事实上,cookie和session是搭配使用的。

浏览器第一次访问网站的时候,cookie是空的,这时网站服务器会生成一个session_id给cookie,同时对应session_id存储这个用户的信息,下次浏览器再访问网站的时候,cookie中就携带了这个session_id,服务器根据session_id来查找用户的信息。可以说cookie就像一个暗号一样,服务器依赖cookie来查找session中的数据。

image-20180812211238385

那我们在后台如何对cookie进行读写呢?

操作

原生node.js通过在响应请求的头信息里设置Set-Cookie来写入cookie信息。我们先在本地服务器查看cookie:

image-20180812213924734

接下来我们来添加自定义的cookie

1
2
3
4
5
6
7
8
const http = require("http");
let server = http.createServer((req, res) => {
res.setHeader('Set-Cookie', ["type=ninja", "language=javascript"]);
res.end("hello world");
})
server.listen(9000, () =>{
console.log("server running at 9000");
})

此时我们再查看cookie,就比之前多了两条设置的内容:

image-20180812214112902

原生的请求对象req没有req.cookies属性,所以我们无法直接获取cookie,需要用到一个中间件cookie-parser

1
2
3
4
5
6
7
8
9
10
11
12
const express = require("express");
const cookieParser = require("cookie-parser");
let server = express();
server.use(cookieParser());
server.use(('./'), (req, res) => {
console.log(req.cookies);
res.cookie("user","nikkkki",{path: '/aaa', maxAge: 30*24*2600*1000});
res.end("hello world");
})
server.listen(9000, ()=>{
console.log("server running at 9000");
})

控制台输出:

image-20180812220929289

Application的cookie列表:

image-20180812220906749

http://localhost:9000/aaa页面的Application中:

image-20180812221217947

可以看到原生node.js和express框架设置cookie的方法略有不同

IMAGE

3. cookie签名

浏览器中的cookie虽然只是用来开启session,但是如果被别人盗取了cookie,同样能以你的身份来获取session数据,因此我们需要对cookie做一些保险处理。其中一个方法是给cookie签名。

1
2
3
4
5
6
7
8
9
10
11
12
13
const express = require("express");
const cookieParser = require("cookie-parser");
let server = express();
server.use(cookieParser('abcdefg'));
server.use('./', (req, res) => {
req.secret = 'abcdefg';
res.cookie('user','nikkkki', {signed: true});
console.log("signed cookie: ", req.signedCookies);
console.log("unsigned cookie: ",req.cookies);
})
server.listen(9000, ()=>{
console.log("server running at 9000");
})

cookieParser中间件可以接受一个字符串作为加密的钥匙,在res.cookie设置cookie值时,加上{signed: true}就会给cookie加密。输出结果:

image-20180812222241810

列表中的cookie则变成了一长串字符串:

image-20180812222410052

但是可以看到,加密后的cookie还是可以轻易分辨出设置的值。实际上,签名无法保证原内容不被看见,只能杜绝修改。

服务器根据秘钥对cookie签名,如果利用同一个秘钥解析出来的cookie和原cookie一样则表明cookie没有被修改过。

在代码中有两个地方都出现了我们设置的签名字符串abcdefg,这两个的作用是不同的。

image-20180812223048200

res.secret用于给cookie签名,而cookieParser()中的字符串用于输出原cookie内容(unsign),如果我们将cookieParser()中的字符串去掉,输出结果就是签名过的cookie:

image-20180812223430581

IMAGE

cookie的httpOnly属性

签名过的cookie还是可以被修改的:

image-20180813101238143

image-20180813101253929

想要cookie无法再浏览器中被修改,可以在服务器端设置cookie的httpOnly属性:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
const express = require("express");
let server = express();
const cookieParser = require("cookie-parser");
server.use(cookieParser('abcdefg'));
server.use((req, res) => {

req.secret = 'abcdefg';
res.cookie("user","nikkkki",{signed: true, httpOnly: true}); //cookie不允许被修改
res.end("hello");
})

server.listen(9000, () =>{
console.log("server running at 9000");
})

我们再尝试在浏览器端修改:

image-20180813101439002

控制台并没有报错,但是修改没有生效:

image-20180813103448467

而且httpOnlytrue的cookie无法在浏览器获取:

image-20180813103747910

4. 清除cookie内容 clearCookie

1
2
3
4
5
6
7
8
9
10
11
const express = require("express");
const cookieParser = require("cookie-parser");
let server = express();
server.use(cookieParser());
server.use('./', (req, res) => {
res.clearCookie('user');
res.end('hello');
})
server.listen(9000, () => {
console.log("server running at 9000");
})

res.clearCookie(key)用于删除对应的cookie值:

image-20180812223956725

res.clearCookie()不会删除所有cookie,如果括号内没有值,则不会删除任何cookie。