cookie
cookie的处理
- 在web应用中,多个请求之间共享“用户会话”是非常必要的。但HTTP协议是无状态的。那这时cookie就出现了。那cookie又是如何处理的呢?
- 服务端向客户端发送cookie
- 客户端的浏览器把Cookie保存
- 然后在每次请求浏览器都会将Cookie发送到服务端
cookie使用的注意事项:
- 可能被客户端篡改,使用前验证其合法性
- 不要存储敏感数据,比如用户密码,账户余额等
- 使用httponly保证安全
- 尽量减少cookie的体积,这是由于浏览器对于每个域名所包含的cookie数和cookie的总大小有一定的限制
向服务端发送cookie
- 在HTML文档被发送之前,Web服务器通过传送HTTP包头中的Set-Cookie消息把一个cookie发送到用户的浏览器中,如下所示:
Set-Cookie:name=zhangjie;Expires=Fri, 01 Jun 2018 07:21:59 GMT
cookie设置中常用属性:
- name=value:键值对,可以设置要保存的Key/Value,注意这里的那么不能和其它的属性项的名字一样
- Expires:过期时间(单位ms),在设置的某个时间点后该Cookie就会失效,例如 Expires=Fri, 01 Jun 2018 07:41:15 GMT,在这个时间点后,cookie就会失效
- Max-Age:最大失效时间(单位s),设置在多长时间之后失效
- Secure: 设置为true时,表示创建的 Cookie 会被以安全的形式向服务器传输,也就是只能在 HTTPS 连接中被浏览器传递到服务器端进行会话验证,如果是 HTTP 连接则不会传递该信息,所以不会被窃取到Cookie 的具体内容。同上,在客户端我们也无法在document.Cookie找到被设置了Secure=true的Cookie键值对。Secure属性是防止信息在传递的过程中被监听捕获后信息泄漏,HttpOnly属性的目的是防止程序获取Cookie后进行攻击。我们可以把Secure=true看成比HttpOnly更严格的访问控制
- path: 指定可访问Cookie的目录。例如:“userId=320; path=/shop”;就表示当前Cookie仅能在shop路径下使用
- HttpOnly: 这是微软对Cookie做的扩展。如果在Cookie中设置了"HttpOnly"属性,那么通过程序(JS脚本、Applet等)将无法读取到Cookie信息,这样能有效的防止XSS攻击
- domain: 指定可访问Cookie的主机名.主机名是指同一个域下的不同主机,例如:www.google.com和gmail.google.com就是两个不同的主机名。默认情况下,一个主机中创建的Cookie在另一个主机下是不能被访问的,但可以通过domain参数来实现对其的控制,其语法格式为:“name=value; domain=CookieDomain”;以google为例,要实现跨主机访问,可以写为:“name=value;domain=.google.com”;这样,所有google.com下的主机都可以访问该Cookie
如下:如果用户为第一次访问,那么输出“欢迎首次访问”,此时服务端已向客户端发送cookie,并且客户端已将cookie进行保存,当再次访问时,cookie将会放置在请求头中发送到服务端,此时输出“欢迎再次访问”
let http = require('http');
let url = require('url');
let querystring = require('querystring');
http.createServer((req,res)=>{
let urlObj = url.parse(req.url,true);
let pathname = urlObj.pathname;
if (pathname === '/cookie'){
res.setHeader('Content-Type','text/plain;charset=utf-8')
let cookie = req.headers.cookie || {};
let cookieObj = querystring.parse(cookie,';'); // 将请求头中的cookie信息转为对象格式
if (cookieObj.isVisited){ // 如果存在cookie那么视为再次访问
res.end('欢迎再次访问');
}else{ // 如果不存在,那么向客户端发送cookie信息
res.setHeader('Set-Cookie','isVisited=1;Max-Age=30');
res.end('欢迎首次访问');
}
}
}).listen('8090');
下面使用cookie-parser重写上面的demo
let express = require('express');
let cookieParser = require('cookie-parser');
let app = express();
app.use(cookieParser());
app.get('/cookie',(req,res)=>{
let cookies = req.cookies||{};
if (cookies.isVisited){
res.send('欢迎再次访问');
}else {
res.cookie('isVisited','1',{maxAge:10*1000});
res.send('欢迎首次访问');
}
});
app.listen('8090');
session
session介绍
cookie 虽然很方便,但是使用 cookie 有一个很大的弊端,cookie 中的所有数据在客户端就可以被修改,数据非常容易被伪造,那么一些重要的数据就不能存放在 cookie 中了,而且如果 cookie 中数据字段太多会影响传输效率。为了解决这些问题,就产生了 session,session 中的数据是保留在服务器端的,那什么是session呢?
- session是一种记录客户端状态的机制,不同的是cookie保存在客户端浏览器中,而session保存在服务器上。
- 客户端浏览器访问服务器的时候,服务器把客户端信息以某种形式记录在服务器上,这就是session。客户端浏览器再次访问时,只需要从该session中查找该客户的状态就可以了。
- 如果说cookie机制是通过检查客户身上的“通行证”来确定客户身份的话,那么session机制就是通过检查服务器上的“客户明细表”来确认客户身份
- session相当于在服务器上建立的一份客户档案,客户来访的时候只需要查询客户档案表就可以了
session处理
- 在服务器端生成全局唯一标识符(session_id)
- 在服务器内存里开辟此session_id对应的数据存储空间
- 将session_id作为全局唯一标识符通过cookie发送到客户端
- 以后客户端再次访问服务器时,会把session_id通过请求头中的cookie发送到服务端
- 服务器再通过session_id把此标识符在服务器端的数据取出
如下demo所示:我们创建了一个会员的信息,如果当前会员不存在,那么创建会员信息,并将会员id即session_id返回,如果会员存在,那么通过session_id获取会员的详细信息:
let http = require('http');
let url = require('url');
let queryString = require('querystring');
let SESSION_KEY = 'session_key';
let session = {};
http.createServer(function(req,res){
let urlObj = url.parse(req.url,true);
let pathname = urlObj.pathname;
let membershipPoint = {point:100};
if (pathname === '/'){
let cookie = req.headers.cookie || {};
let cookieObj = queryString.parse(cookie,';');
if (cookieObj[SESSION_KEY]){ // 如果当前session_key存在,那么为老会员,并通过session_id获取其会员的会员详细信息
let curSessionObj = session[cookieObj[SESSION_KEY]];
res.setHeader('Content-Type','text/html;charset=utf-8');
if (curSessionObj.point === 0 || curSessionObj.point < 0){
res.end('欢迎您,老朋友,您的余额已使用完毕');
}else {
curSessionObj.point -= 10;
res.end('欢迎您,老朋友,再次光临,余额为'+curSessionObj.point);
}
}else{ // 如果当前session_key不存在,那么为新会员,并建立其会员卡,余额为100,并将会员id即session_id返回给客户端
let session_id = Date.now()+Math.random();
session[session_id] = membershipPoint;
res.setHeader('Set-Cookie',SESSION_KEY+'='+session_id);
res.setHeader('Content-Type','text/html;charset=utf-8');
res.end('欢迎您,新朋友,送您会员卡,余额为'+session[session_id].point);
}
}
}).listen('8090');
express-session
express中ssession的操作需要使用express-session这个模块,主要方法是session(options),其中option中包含可选参数,如下:
- name:设置cookie中,保存session的字段名称,默认为:connect.sid
- store:session的存储方式,默认存放在内存中, 也可以使用 redis,mongodb 等。express 生态中都有相应模块的支持
- secret: 通过设置的 secret 字符串,来计算 hash 值并放在 cookie 中,使产生的 signedCookie 防篡改
- cookie: 设置存放 session id 的 cookie 的相关选项,默认为
- (default: { path: ‘/’, httpOnly: true, secure: false, maxAge: null })
- genid: 产生一个新的 session_id 时,所使用的函数, 默认使用 uid2 这个 npm 包
- rolling: 每个请求都重新设置一个 cookie,默认为 false
- resave: 即使 session 没有被修改,也保存 session 值,默认为 true
- saveUninitialized:保存新创建但未修改的session
使用express-session重写上面的demo,如下:
let express = require('express');
let session = require('express-session');
let app = express();
let SESSION_KEY = 'session_key';
app.use(session({
name:SESSION_KEY,
resave:true, // 每次请求结束,都要重新保存,不管有没有修改
saveUninitialized:true, //保存未修改过的session
secret:'somesecrettoken' // 加密的密钥
}));
app.get('/session',(req,res)=>{
if (req.session.point != undefined){
if (req.session.point === 0 || req.session.point < 0){
res.send('欢迎您,老朋友,您的余额已使用完毕');
}else {
req.session.point -= 10;
res.send('欢迎您,老朋友,再次光临,余额为'+req.session.point);
}
}else{
req.session.point = 100;
res.send('欢迎您,新朋友,送您会员卡,余额为'+req.session.point);
}
});
app.listen(8090);
cookie签名:
cookie 虽然很方便,但是使用 cookie 有一个很大的弊端,cookie 中的所有数据在客户端就可以被修改,数据非常容易被伪造,那么为了数据不被篡改,我们对cookie进行签名, 签名的作用是让服务端知道cookie有没有被修改
const express = require('express');
const cookieParser = require('cookie-parser');
//随机生成的字符串
var signStr = 'xadsafeowirw';
var app = express();
//需要将密匙传给cookieParser, 在接收数据的时候,进行解析。
app.use(cookieParser(signStr));
app.use('/', function (req, res) {
//将密匙字符串赋值给req.secret,可以省略,在上面cookieparser()时会自动对secret赋值
req.secret=signStr;
//返回给浏览器的cookie, 这就是传说中的种cookie了
//如果需要开启签名,第三个参数对象signed 设置为true.
//由于cookie的大小限制4k,而签名后的cookie体积会增加,所以重要的cookie才签名
res.cookie('cookiename', 'liwen', {signed: true, maxAge: 30*1000})
//有没有签名的cookie,获取方式不一样。
console.log('无签名', req.cookies); // 无签名的cookie获取方式
console.log('带签名',req.signedCookies); // 带签名的cookie获取方式
res.send('ok')
})
app.listen(8090);
个人学习笔记,如有错误或不准确之处,请大神指出,感谢~