body-parser
它的作用是对post请求的请求体进行解析,body-parser其实就是当请求发送到服务器时,我们要解析请求,因为请求可以是采用不同请求类型、不同编码、不同压缩类型,所以解析时就要根据不同情况来做不同的解析操作,最终获得客户端发送过来的数据,它的源代码主要做了:
-
处理不同类型的请求体(Content-Type),比如text,json,urlencoded等,对应的报文主体的格式不同
-
处理不同的编码(charset),比如uft8,gbk等
-
处理不同的压缩类型(Content-Encoding),比如gzip,deflate
-
其他边界、异常的处理 具体的源代码分析可以看这篇文章Express常用中间件body-parser实现解析
具体用法参考官方文档 body-parser body-parser主要用来解析四种请求类型的请求体:
-
JSON body
-
Raw body
-
Text body
-
URL-encoded form body
一般用法:
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
复制代码
这个模块不能处理复杂的请求体,此时我们可能要考虑下面的模块:
cookie-parser
- 为什么说要利用签名防止cookie被恶意篡改
我们在浏览器输入用户名和密码发送post请求到后端服务器,后端服务器验证合法,返回响应,并Set-Cookie
为sessionid=***;username=water
,然后浏览器接受到响应发Set-Cookie
,于是将其存入内存或硬盘中;浏览器端再次发起请求,带上Cookie信息sessionid=***;username=water
,请求修改自己的头像信息,服务器根据sessionid
验证当前用户已登录,根据username
,查找数据库中的对应数据,修改头像信息,这是一个正常的cookie设置与利用cookie的过程。但是我们为什么说要防止Cookie被篡改呢?这是因为cookie是存储在客户端的,这时用户可以任意修改cookie值,比如如果当前用户知道username
的作用,修改username=fire
,根据username
,查找数据库中的对应数据,并修改了头像信息,这样就暴露出数据被恶意篡改的风险。
其实就是服务端无法保证张三请求修改数据时到底是不是张三自己要求修改,也可能是李四是在恶意篡改张三的数据。
这时我们就要给cookie增加签名,比如服务器接收到请求中的Cookie项username=fire||34sdklkas
,然后使用签名生成算法secret(fire)=666
,得到的签名666
和请求中数据的签名不一致,则证明数据被篡改,不予通过。
所以cookie中不应该存储敏感数据,应该根据SessionID将敏感数据存储在后端,取数据时根据SessionID去后端服务器获取,对于一些重要的Cookie项,应该生成对应的签名来反之被恶意篡改。
具体可以参考这篇文章 Cookie防篡改机制
- 签名就能够确保安全吗 我们只通过用户名这个cookie来判断登录的是哪一个用户,虽然增加了签名,而且秘钥我们也不知道,看起来很难伪造签名cookie,但是只要原始值相同的情况下,签名也是相同的,这汇总情况下就很容易伪造了,而且我们要确保秘钥的生成算法不被泄露。
- Express中cookie-parser中间件的使用
cookie-parser中间件用来对cookie进行解析,主要包括普通cookie的解析和签名cookie的解析。
- 简单用法
最简单的使用就是cookie的设置与解析,cookie的设置使用res.cookie方法,cookie的解析使用cookie-parser中间件
var express = require('express');
var cookieParser = require('cookie-parser');
var app = express();
app.use(cookieParser());
app.use(function (req, res, next) {
console.log(req.cookies.nick); // 第二次访问,输出chyingp
next();
});
app.use(function (req, res, next) {
res.cookie('nick', 'chyingp');
res.end('ok');
});
app.listen(3000);
复制代码
当前情况下cookie-parser中间件大致实现如下:
app.use(function (req, res, next) {
req.cookies = cookie.parse(req.headers.cookie);
next();
});
复制代码
-
cookie签名、解析
出于安全的考虑,我们通常需要对cookie进行签名
主要要注意一下几点:
cookieParser
初始化时,传入secret
作为签名的秘钥。- 设置cookie时,将
signed
设置为true
,表示对cookie进行签名。 - 获取cookie时,可以同时通过
req.cookies
,也可以通过req.signedCookies
获取。
var express = require('express');
var cookieParser = require('cookie-parser');
var app = express();
// 初始化中间件,传入的第一个参数为singed secret
app.use(cookieParser('secret'));
app.use(function (req, res, next) {
console.log(req.cookies.nick); // chyingp
console.log(req.signedCookies.nick); // chyingp
next();
});
app.use(function (req, res, next) {
// 传入第三个参数 {signed: true},表示要对cookie进行摘要计算
res.cookie('nick', 'chyingp', {signed: true});
res.end('ok');
});
app.listen(3000);
复制代码
签名前的cookie值为chyingp
,签名后的cookie值为s%3Achyingp.uVofnk6k%2B9mHQpdPlQeOfjM8B5oa6mppny9d%2BmG9rD0
,但是这个在我们使用时是不用知道的。
源代码的分析可以参考 cookie-parser-deep-in
morgan
morgan是记录日志的中间件
var express = require('express');
var app = express();
var morgan = require('morgan');
app.use(morgan('short'));
app.use(function(req, res, next){
res.send('ok');
});
app.listen(3000);
复制代码
node basic.js
运行程序,并在浏览器里访问 http://127.0.0.1:3000 ,打印日志如下
➜ 2016.12.11-advanced-morgan git:(master) ✗ node basic.js
::ffff:127.0.0.1 - GET / HTTP/1.1 304 - - 3.019 ms
::ffff:127.0.0.1 - GET /favicon.ico HTTP/1.1 200 2 - 0.984 ms
复制代码
核心API
morgan()返回一个express日志中间件
morgan(format,options)
复制代码
- format:可选,morgan定义了几种日志格式,每种格式都有对应的名称,比如combined,short等,默认是default
- options:可选,包括stream,skip,immediate
- stream:日志的输出流配置,默认是process.stdout
- skip:是否跳过日志记录
- immediate:布尔值,默认是false。当为true时,一收到请求就记录日志;如果为false,则在请求返回后再记录日志
将日志打印到本地文件
morgan支持stream配置项,可以用它来实现将日志存储到本地文件
var express = require('express');
var app = express();
var morgan = require('morgan');
var fs = require('fs');
var path = require('path');
var accessLogStream = fs.createWriteStream(path.join(__dirname, 'access.log'), {flags: 'a'});
app.use(morgan('short', {stream: accessLogStream}));
app.use(function(req, res, next){
res.send('ok');
});
app.listen(3000);
复制代码
具体的源代码分析可以参考这篇文章 日志模块morgan