Express之cookie session研究
前言
最近在研究Express的源码,查看他的依赖项,关于cookie与session的依赖项一共有5个,这些依赖项具体都是做什么的呢,于是我就去研究了一下。
准备知识
http cookie
HTTP Cookie(也叫Web Cookie或浏览器Cookie)是服务器发送到用户浏览器并保存在本地的一小块数据,它会在浏览器下次向同一服务器再发起请求时被携带并发送到服务器上。
当服务器收到HTTP请求时,服务器可以在响应头里面添加一个Set-Cookie
选项。浏览器收到响应后通常会保存下Cookie,之后对该服务器每一次请求中都通过Cookie请求头部将Cookie信息发送给服务器。另外,Cookie的过期时间、域、路径、有效期、适用站点都可以根据需要来指定。
Set-Cookie: <cookie名>=<cookie值>
返回信息如下:
HTTP/1.0 200 OK
Content-type: text/html
Set-Cookie: yummy_cookie=choco
Set-Cookie: tasty_cookie=strawberry
...
[页面内容]
现在再向服务端发起请求则请求会如下:
GET /sample_page.html HTTP/1.1
Host: www.example.org
Cookie: yummy_cookie=choco; tasty_cookie=strawberry
标记为 Secure 的Cookie只应通过被HTTPS协议加密过的请求发送给服务端。但即便设置了 Secure 标记,敏感信息也不应该通过Cookie传输,因为Cookie有其固有的不安全性,Secure 标记也无法提供确实的安全保障。从 Chrome 52 和 Firefox 52 开始,不安全的站点(http:)无法使用Cookie的 Secure 标记。
为避免跨域脚本 (XSS) 攻击,通过JavaScript的 Document.cookie API无法访问带有 HttpOnly 标记的Cookie,它们只应该发送给服务端。如果包含服务端 Session 信息的 Cookie 不想被客户端 JavaScript 脚本调用,那么就应该为其设置 HttpOnly 标记。
Domain 和 Path 标识定义了Cookie的作用域:即Cookie应该发送给哪些URL。
Domain 标识指定了哪些主机可以接受Cookie。如果不指定,默认为当前文档的主机(不包含子域名)。如果指定了Domain,则一般包含子域名。
例如,如果设置 Domain=mozilla.org,则Cookie也包含在子域名中(如developer.mozilla.org)。
Path 标识指定了主机下的哪些路径可以接受Cookie(该URL路径必须存在于请求URL中)。以字符 %x2F ("/") 作为路径分隔符,子路径也会被匹配。
例如,设置 Path=/docs
,则以下地址都会匹配:
/docs
/docs/Web/
/docs/Web/HTTP
set-cookie
响应首部 Set-Cookie
被用来由服务器端向客户端发送 cookie。
语法:
Set-Cookie: <cookie-name>=<cookie-value>;[options]
其中options
可以有下列这些值:
Expires=<date>
过期时间Max-Age=<non-zero-digit>
cookie失效之前需要经过的秒数Domain=<domain-value>
主机名Path=<path-value>
路径名Secure
一个带有安全属性的 cookie 只有在请求使用SSL和HTTPS协议的时候才会被发送到服务器HttpOnly
设置了 HttpOnly 属性的 cookie 不能使用 JavaScript 经由 Document.cookie 属性、XMLHttpRequest 和 Request APIs 进行访问,以防范跨站脚本攻击(XSS)SameSite
允许服务器设定一则 cookie 不随着跨域请求一起发送
实例:
Set-Cookie: id=a3fWa; Expires=Wed, 21 Oct 2015 07:28:00 GMT; Secure; HttpOnly
Express依赖项研究
cookie
这个库主要是用来解析或者序列化cookie的信息的,API也只有两个一个是cookie.parse(str, options)
,它的options只有一个选项decode
用来规定采用何种解码方法,一个是cookie.serialize(name, value, options)
它的options和set-cookie
的可选项一致。
cookie-signature
使用Nodejs自带的crypto模块签名和解析签名过的cookie,使用方法:
var cookie = require('cookie-signature');
var val = cookie.sign('hello', 'tobiiscool');
val.should.equal('hello.DGDUkGlIkCzPz+C0B064FNgHdEjox7ch8tOBGslZ5QI');
var val = cookie.sign('hello', 'tobiiscool');
cookie.unsign(val, 'tobiiscool').should.equal('hello');
cookie.unsign(val, 'luna').should.be.false;
cookie-parser
cookie-parser
依赖了cookie
和cookie-signature
,如果用户在调用的时候传入了secret
的话,则会生成signedCookie,否则为普通的json的cookie的键值对。
使用方法:
var express = require('express')
var cookieParser = require('cookie-parser')
var app = express()
app.use(cookieParser())
API:
cookieParser(secret, options)
secret
可以为数组或者字符串,options等同于cookie.parse
传入的可选项cookieParser.JSONCookie(str)
将字符串解析为json形式cookieParser.JSONCookies(cookies)
将给定对象重复使用JSONCookie将value值替换为解析值cookieParser.signedCookie(str, secret)
解析signedCookie值cookieParser.signedCookies(cookies, secret)
将给定的对象重读使用signedCookie将value值替换为解析值
express-session
这个中间件将session保存在服务端,将对应的session ID保存在cookie中,默认地是将session保存在内存中,不能在生产环境中使用,会引起内存泄漏,可以选择使用session-store
将之存储在数据库中。使用方法:
var session = require('express-session')
var express = require('express')
var app = express()
app.use(session({
secret:"huxinmin", //签名使用
cookie:{ //"参照cookie中的设置",
domain:
expires:
httpOnly:
maxAge:
path:
sameSite:
secure:
},
genid:function(req){ //生成session ID的方法,默认使用uid-safe
},
name:"", //session ID的名字,默认是connect.sid
proxy:undefined, //当设置了cookies secure选项时,信任的代理服务器
resave:true, //强制保存session到session store即使没有任何改变
rolling:false, //强制在每一次responce时重新设置cookie的识别符,例如maxAge
saveUninitialized:true, //是否自动保存未初始化的会话
store: new MemoryStore(), //session存储的实例
unset:'keep', //响应结束后是否保存存储的session
}))
创建成功后,就可以在req.session
中访问到session了,同时它上面还有如下这些方法或属性:
- regenerate(callback)重新生成session
- destroy(callback)销毁
- reload(callback)重载
- save(callback)将session保存至store中
- touch()更新
.maxAge
属性 - id session ID的别名,req.sessionID中保存的是session ID
- cookie伴随session的cookie
除了这些,你还可以自定义你自己的属性或方法,只要挂载到req.session
上即可。
cookie-session
这个插件会将session的信息存储在cookie中,不过不能超过4KB大小。
使用方法:
var cookieSession = require('cookie-session')
var express = require('express')
var app = express()
app.use(cookieSession({
name: 'session',
keys: [/* secret keys */], //或者使用字符串的scret:""
// Cookie 选项,除了包含常见的expires,path,domain,等还包括signed和overwrite
maxAge: 24 * 60 * 60 * 1000, // 24 hours
signed:true,//是否签名的cookie
overwrite:true //设置cookie之前是否重写
}))
然后你就能在req.session
中访问到了创建的session
,并且还有下面几个属性:
req.sessionOptions.isChanged
判断session是否变化req.sessionOptions.isNew
判断session是否是新的req.sessionOptions.isPopulated
判断session是否有填充数据req.sessionOptions
创建session时的选项的一个浅拷贝req.session = null
可以销毁session