上一节-node.js学习(10)—express中间件—body-parser获取post、get数据
上节我们讲了如何在express中获取post、get数据,这一节我们讲解cookie和session。
20190523164210.png
1.给浏览器发送( response ) cookie
我们在server.js添加如下代码
const express=require('express');
const server=express();
server.use('/test', function (req, res){
if (req.url === '/favicon.ico') {
return
}
res.cookie('user', 'blue'); //发送cookie给浏览器,让浏览器来写入cookie
res.send('hello cookie');
});
server.listen(8080);
- 在express中,它已经为我们封装好了操作cookie的方法,我们使用
res.cookie
来发送cookie - 上面代码表示在
/test
路径下,设置cookie为key:user,value:blue
- 如果设置多个cookie,需要写多行
res.cookie
- 除下设置key/value,还可以添加其他参数,如path,http,domain.maxAge,expires等,具体可以看express文档-res.cookie
res.cookie('user', 'blue',{
path:'/',
httpOnly:false
...
})
启动服务,打开浏览器并访问/test
路径,我们可以在控制台找到我们刚设置的cookie。
2.服务器接收浏览器传过来的( request ) cookie
上面我们讲了服务端如何发送cookie给浏览器,并且也知道浏览器访问服务器url,会先检查url下有没有cookie,有的话在访问过程中,会将cookie自动添加到请求头中,那么服务器如何从请求头中获取cookie呢?
我们修改server.js
const express=require('express');
const cookieParser=require('cookie-parser');
const server=express();
server.use(cookieParser())
server.use('/test', function (req, res){
if (req.url === '/favicon.ico') {
return
}
res.cookie('user', 'blue'); //设置cookie
res.cookie('user1', 'blue1'); //设置cookie
console.log(req.cookies) //{"user": "blue","user1":"blue1"}
res.send(req.cookies);
});
server.listen(8080);
- express默认需要引入
cookie-parser
模块(安装express会自动安装,不用单独安装,只需要引入即可)才能获取浏览器传过来的cookie。 - 使用
req.cookies
获取浏览器传过来的cookie,值为对象。
在浏览器中运行上面代码,如下图:
- 第一次req.cookies为空,浏览器写入cookie
- 第二次浏览器发送之前写入的cookie,req.cookies有值
3.cookie签名(防服务器数据篡改)
何谓篡改,我们都知道,cookie是保存在客户端的,我们可以通过js或者直接在控制台修改cookie的,这样会暴露一个问题:
- 浏览器发起一个修改用户信息的请求,带上Cookie信息username=aa(前提已经种下cookie),服务器接收到请求后,会去修改username为aa的数据
- 如果用户知道username字段表示用户名信息,那么他把值改为bb,然后再次发起请求,服务器接收到请求,去修改username为bb用户的数据(假如数据库中刚好有个username为bb的用户)
- 这样因为cookie被用户修改,导致服务器数据被篡改的风险
怎么防止篡改?就有了cookie签名的由来。
- 假设现在有个cookie(username=blue),服务器在发送cookie给浏览器之前先配置一个秘钥(如sdfjk7777),通过sha256或者其他加密算法(这里假设加密方法为secret()),然后使用
secret('blue','sdfjk7777')
生成一个签名和cookie值组合(类似{username:s:blue.3byMSHd31XkJ0JszpW7IXndQaUh6XFLxks+6/zWRdXg}
- 其中blue表示真正的值,后面是签名。假如我现在把blue改成blue1。
- 当浏览器再次发送请求时,服务器取出签名(因为太长了,这里叫A)以及cookie真正的值(blue1)。然后使用之前的加密算法判断secret('blue1','sdfjk7777')==A,如果不相等,说明cookie被恶意修改了。
4.使用cookie-parser增加cookie签名
我们修改server.js
const express=require('express');
const cookieParser=require('cookie-parser');
const server=express();
//增加一个随机秘钥
server.use(cookieParser('asdfd45556'))
server.use('/test', function (req, res){
if (req.url === '/favicon.ico') {
return
}
res.cookie('user', 'blue',{
signed:true
});
res.cookie('user1', 'blue1');
console.log(req.cookies); //无签名cookie {user1:blue1}
console.log(req.signedCookies); //签名cookie {user:blue}
console.log(req.headers.cookie) //浏览器发过来的cookie: user1=blue1; user=s%3Ablue.3byMSHd31XkJ0JszpW7IXndQaUh6XFLxks%2B6%2FzWRdXg
res.send('ok'); //由于res.send默认最后一个生效(相当于只能写一个),所以上面用console.log代替输出
});
server.listen(8080);
- 将秘钥
asdfd45556
传入cookieParser函数中,然后在需要签名的cookie增加参数signed:true
- 此时cookie会分割成两个属性 cookies-未签名的、signedCookies-已签名的
- 注意:req.signCookies的值为签名的cookie原始值,不带签名字符串,阅读cookie-parser源码,其将签名去除然后再赋到signCookies上
- 可以通过req.header.cookie获取浏览器带过来的cookie
上图中,s%3Ablue.3byMSHd31XkJ0JszpW7IXndQaUh6XFLxks%2B6%2FzWRdXg为签名cookie,实际上是该值是先签名后encodeURIComponent编码过来的。我们可以解码看
console.log(decodeURIComponent('s%3Ablue.3byMSHd31XkJ0JszpW7IXndQaUh6XFLxks%2B6%2FzWRdXg'))
//s:blue.3byMSHd31XkJ0JszpW7IXndQaUh6XFLxks+6/zWRdXg
其中,s:
表示签名,blue
为真正的值,后面的是签名
所以我们在浏览器实际上仍可以看见用户信息,cookie签名并不能起到加密作用,只是防篡改作用。
5.cookie-session
之前在cookie和session区别曾讲过seesion。下面我们通过nodejs来实现session的demo(判断用户是否来过,保持用户登陆状态)
server.js代码如下:
const express=require('express');
// const cookieParser=require('cookie-parser');
const cookieSession=require('cookie-session');
const server=express();
// server.use(cookieParser())
server.use(cookieSession({
keys: ['aaa', 'bbb', 'ccc'], //session用来生成签名的列表,必填
name: 'sessionId', //session名字,可省略,实测默认名字为'express:sess'
cookie:{ //session配置
//cookie选项
path:'',
...
}
}))
server.get('/test', function (req, res){
if (req.url === '/favicon.ico') {
return
}
console.log(req.session); //第一次为{},后面为{hasCome:true}
// console.log(req.cookies);
// console.log(req.signedCookies);
if(!req.session['hasCome']){
req.session['hasCome']=true //写入hasCome属性
res.send('欢迎第一次');
}else{
res.send('欢迎再次来');
}
});
server.listen(8080);
- 使用session依赖
cookie-session
中间件,和cookie-parse不一样,需要单独安装,express不带。 - 该中间件使用必须传一个key数组,用来的session进行签名,和cookie签名一样,用来防止篡改,校验。name可以不传默认为
express:sess
- 使用该中间件,session保存在cookie中,所以额外配置项就是cookie选项。所以如果expires/maxAge不传,即N/A,会话cookie,浏览器关闭即过期。
- 引入该中间件,req上面会增加session属性。demo中第一次进来session为空,不存在
hasCome
属性(判断是否已经来过),所以写入该属性,页面返回欢迎第一次
,后面再次访问,req.session为{hasCome:true}
,页面返回欢迎再次来
,可以看下图req.session
打印状态以及页面展示内容
- 我们通过
req.session
可以很方便的访问session,由于该session对应的id是保存在cookie中,所以我们仍可以引用cookie-parser在cookie中查看sessionId(注意:虽然sessionId有签名,但是该插件将sessionId存在req.cookies
属性中,不在req.signedCookies
中,所以通过req.signedCookies
是无法访问到的)
现在我们把之前server.js的代码中注释的cookie-parser等代码解开注释,可以看到打印结果:
console.log(req.cookies)
//结果为
{
sessionId: 'eyJoYXNDb21lIjp0cnVlfQ==',
'sessionId.sig': 'moOOM66p-CAR9aeTORKkuBYXFk4'
}
console.log(req.signCookies)
//结果为
{}
- 从session的格式来看,签名是单独为一个
sessionId.sig
字段,和之前使用cookie-parser签名一个cookie不一样(之前直接在cookie上拼接签名)
- 如果
session
值变化,sessionId
以及签名sessionId.sig
会跟着变化。我们再换个demo(记录页面访问次数)测试下
更改server.js代码:
const express=require('express');
const cookieSession=require('cookie-session');
const server=express();
server.use(cookieSession({
keys: ['aaa', 'bbb', 'ccc'],
name: 'sessionId',
}))
server.get('/test', function (req, res){
if (req.url === '/favicon.ico') {
return
}
//count字段表示页面访问次数
if(!req.session['count']){
req.session['count']=1
}else{
req.session['count']++
}
res.send(''+req.session['count'])
});
server.listen(8080);
页面测试结果:
count1.gif
网友评论