怠惰了一周真是有点方,看文档都晕乎乎的。整理一下和cookie、session相关的知识。并使用这些内容实现保持登录状态。
更新
2018-5-23
参考资料:
所使用模块版本
express: 4.16.2
cookie-parser: 1.4.3
express-session: 1.15.6
connect-mongo: 2.0.0
cookie
cookie的一点知识
cookie
储存在客户端,且有大小限制。不可跨域。访问网站时若有可用的cookie
会附带在http
请求报文中。浏览器对cookie
有大小和个数限制。
设置和清除cookie
express
本身就提供设置和清除cookie
的方法。
res.cookie( name , value [,options] )
可以设置cookie
其中name
类型为string
。value
为cookie
所储存的数据,可使用string
和object
。
如果value
为string
则默认原样发送。若为object
则默认使用JSON.stringify
处理后再发送(和主动使用有区别,默认处理后,报文中的数据前多了j:
)。cookie
在报文中显示格式为name1=value1;name2=value2
。报文中数据编码方式与对URL
的编码相似(未详细考究,已知不安全字符和中文的处理方式相同),都是编码后使用percent-encoded
表示。
options
为object
。常用设置如下:
httpOnly
:可选值true
/false
,默认为false
。为true
时无法用js
获取到该cookie
maxAge
:设置cookie
有效时间,单位为毫秒。path
:设置有效路径,默认为/
。当为默认值时,对所有页面有效,即所有页面均能获取该cookie
res.clearCookie( name [,options])
可以删除cookie
name
为cookie
的标识。options
基本不会用到。
关于设置、删除cookie
的详细参数配置可看这里
解析cookie
需要引入cookie-parser中间件。然后在路由中直接使用req.cookies
即可。
req.cookies
获取到的是cookie
组成的对象。
关于signed cookie
设置signed cookie
首先需要使用cookie-parser
中间件,并传入secret
(即签名字符,string
类型)。然后在res.cookie
的options
中设置signed:true
,就会使用secret
对cookie
进行签名。
删除signed cookie
也是用res.clearCookie()
解析signed cookie
也需要使用cookie-parser
中间件,然后在路由中用req.signedCookies
获取,得到一个对象。
cookie-parser
对cookie
如何签名的详细请阅读相关源码或自行搜索(
session
需要引入express-session中间件,能解析、设置和删除session
。
注:1.5.0
版本后已经不需要额外引入cookie-parser
中间件。
使用express-session中间件
1 | const session = require('express-session') |
注:express-session
强制要求使用secret
对cookie
进行签名,且只需设置secret
,cookie
中不需要设置signed:true
。
session
中传入参数详见文档
session的设置和删除。
设置。使用中间件app.use(session({...}))
后,在路由中使用req.session.anyKey
进行读写。其值可设置为string
或object
。若没有设定任何值,且设置中saveUninitialized: false
,则session
不会被保存。
删除。使用req.session.destroy((err)=>{...})
即可清除session
。若想同时删除浏览器中的session cookie
,可以这样。1
2
3
4req.session.destory((err)=>{
...
res.clearCookie('cookieName')
})
session的储存
session
可以存在内存、cookie本身、redis等缓存、数据库中。express-session
默认储存在内存中。
内存和cookie本身不太合适。储存在内存中无法在多进程时共享数据,关闭服务器后数据丢失。储存在cookie本身中有安全问题且增加了传输的数据量。
redis等缓存可以共享状态(多进程时),速度快。redis
是一个基于内存的键值数据库,支持共享数据,但数据量过大时无法单独使用。
数据库可保存足够多的数据,缺点是查找速度较缓存慢。
使用connect-mongo将session存入mongodb
首先声明redis
是基于内存的,速度肯定比存mongodb
快。考虑到数据量过大时,单redis
会有数据丢失,由于学生机,本人选择用mongodb
。(更好的做法是redis + mongodb
,待填坑)
需要先引入connect-mongo
1 | const session = require('express-session') |
然后在express-session
中间件中进行设置。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17app.use(
session({
secret: '1234',
store: new mongoStore({
url: 'mongodb://localhost/test', // mongodb数据库地址。
}),
saveUninitialized: false,
resave: false,
cookie: {
httpOnly: true,
// maxAge设置了cookie和session的有效时间。(小于一分钟对session无效
maxAge: 1000 * 60 * 60 * 24
}
})
)
更多请阅读文档
session验证登陆状态
对于没有携带session cookie
的访问,express-session
都会自动给予唯一的ID
。访问方式包括GET
和POST
,其他未测试。
在接收到用户账号密码后,进行验证,验证完毕将用户信息存入session
,比如req.session.user = user._id
。在下一次访问中session.user
的值不为undefine
且在数据库中有对应数据,可确认已登录。
使用express-session
产生的signed cookie
发送到客户端,同一个用户每次手动登录客户端收到的cookie
内容都不同,故不会产生一次被盗,永远被控制的问题。但在session
有效期内依然会有被盗取cookie
后的安全问题,但此为环境本身不安全,无法解决。
同时,后端修改session
存储的数据也不会使cookie
发生变化。也即,自动生成的session cookie
仅携带sessionID
。缺点为所有数据都保存在后端,若要为cookie
附加其他数据,需要手动创建新的cookie
。