作者:烨竹
cookie简介:
cookie 是 http 协议的一部分,它的处理分为如下几步:
服务器向客户端发送 cookie。
通常使用 HTTP 协议规定的 set-cookie 头操作。
规范规定 cookie 的格式为 name = value 格式,且必须包含这部分。
浏览器将 cookie 保存。
每次请求浏览器都会将 cookie 发向服务器。
其他可选的 cookie 参数会影响将 cookie 发送给服务器端的过程,主要有以下几种:
path:表示 cookie 影响到的路径,匹配该路径才发送这个 cookie。
expires 和 maxAge:告诉浏览器这个 cookie 什么时候过期,expires 是 UTC 格式时间,maxAge 是 cookie 多久后过期的相对时间。当不设置这两个选项时,会产生 session cookie,session cookie 是 transient 的,当用户关闭浏览器时,就被清除。一般用来保存 session 的 session_id。
secure:当 secure 值为 true 时,cookie 在 HTTP 中是无效,在 HTTPS 中才有效。
httpOnly:浏览器不允许脚本操作 document.cookie 去更改 cookie。一般情况下都应该设置这个为 true,这样可以避免被 xss 攻击拿到 cookie。
具体参考:
http://expressjs.com/zh-tw/4x/api.html#res
http://wiki.jikexueyuan.com/project/node-lessons/cookie-session.html
https://blog.csdn.net/kuangshp128/article/details/75152458
https://cnodejs.org/topic/5212d82d0a746c580b43d948
Cookie在express上的应用
首先,我们进入express的官网看到,它提供了几个cookie相关的API参考:
Response (server设定cookie,回传给client)
res.cookie(name,value [,options])
将cookie设置name为value。该value参数可能是一个字符串或对象转换为JSON。
该options参数是一个可以具有以下属性的对象
res.clearCookie(name [,options])清除指定的cookie
Request (向client接收cookie)
req.signedCookies
取得由client端接收过来有设定签章的请求,让它形成未签章并加以使用。
有加上签章的cookie,好处是,只有开发者自己知道的签章加上回应出去给个别oookie时,使用者根本不知道你开发者签了什么,要窜改也难。否则,恶意攻击容易放入req.cookie。
ex.接收过来的Cookie: user= ladykaka .CP7AWaXDfAKIRfH49dQzKJx7sKzzSoPq7/AcBBRVwlI3
当后面这一串.CP7AWaXDfAKIRfH49dQzKJx7sKzzSoPq7/AcBBRVwlI3签章与开发人员设定的签章有相应,req.signedCookies.user的值即为: ladykaka
设定签章
express设定签章,必须使用cookie-parser middleware,它的作用是即为cookie做一个签章。
用法:在你的express物件,加入cookieParser(your sign)
即可!
参数your sign建议是一个128 bytes的随机字串。
var app=express();
app.use(cookieParser('123456789'));
登陆功能:
建立login文件夹,并且初始化
mkdir login
cd login
npm init
初始化参数:
package name:你这个Project要叫什么名字
version:你决定这个Project现在该是第几版
description: Project基本介绍
entry point:进入点,如果要跑你的Project应该要执行哪个档案
author:作者(自己)
license:你这个Project是采用什么授权的
test command:这个不太重要,待会会说明
创建相关模块:
npm install express --save
npm install body-parser --save
npm install cookie-parser --save
npm install pug --save
/public/cookie下建立好静态网页
view下建立pug样板
//index.pug
extends layout
block content
h1= title
p hello #{member}
if logstatus == false
a(href="Login.html") 登入
else
a(href="./logout") 登出
//layout.pug
doctype html
html
head
title= title
link(rel='stylesheet', href='/stylesheets/style.css')
body
block content
//error.pug
extends layout
block content
h1= message
h2= error.status
pre #{error.stack}
建立express web server
//載入第三方模組
var express = require('express');
var cookieParser = require('cookie-parser');
var bodyParser = require('body-parser');
//载入路由档案
var routerCookie=require('./routes/loginAPI');
var app = express(); //建立一个express物件
//set view engine
app.set("view engine","pug")
//set view directory
app.set("views",__dirname+"/views")
//將request进來的data 转成 json()
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
// Create a router to handle routes for a set of loginAPI
// 抓出來, 变成独立档案
// -------------------------------------------------------
//静态档案 like .js, .json, .xml, html....
app.use(express.static(__dirname+'/public'));
// 允许 /cookie 使用這個路由
app.use('/cookie', routerCookie);
app.listen(3000,function(){
console.log('Ready...for 3000,二哈!!!');
});
重头戏来了
上方index.js下面的转json数据之后,加入一个签章:
// sign for cookie
app.use(cookieParser('123456789'));
建立路由档案,新建routes文件夹,在下面添加loginAPI.js文件
var express = require("express");
var loginAPI =express.Router();
//进入需要验证的界面
loginAPI.get('/', function(req, res) {
//一开始预设,所有的登入状态isLogin都是false,预设的登入者是guest。
//如果,我们接收到的cookie皆存在,则改变登入者姓名及登入状态。
//然而,无论有没有登入,皆会导到index.jade样版,去做呈现。
var name='guest';
isLogin=false;
if(req.signedCookies.firstName && req.signedCookies.lastName){
name=req.signedCookies.firstName+ ' '+req.signedCookies.lastName;
isLogin = true;
}
res.render('index', { title: 'Express', member:name, logstatus:isLogin });
});
//如果表单送出后,只要fristName, lastName其中一个栏位没有填写,则再重回登入页
//如果都有填了,则可以建立cookie,在/cookie之下有效,使用签章,cookie生存值100分钟。
//建好以后,导向到/cookie即….进入需要验证的页面
loginAPI.post('/post', function(req, res) {
// ...
if(req.body.firstName=="" || req.body.lastName=="")
{
return res.redirect('Login.html');
}else{
res.cookie('firstName', req.body.firstName, { path: '/cookie', signed: true, maxAge:600000}); //set cookie
res.cookie('lastName', req.body.lastName, { path: '/cookie', signed: true, maxAge:600000 }); //set cookie
return res.redirect('/cookie');
}
});
// 登出...
loginAPI.get('/logout', function(req, res) {
// ...登出清理cookie,并返回路由、cookie
res.clearCookie('firstName',{path:'/cookie'});
res.clearCookie('lastName',{path:'/cookie'});
return res.redirect('/cookie');
});
module.exports = loginAPI;
做完后
目录结构如下图:
访问效果如下:
session
session store 使用
因为session是储存在server端的,所以,我们可以为Session设置存放位置。
一般而言,session可以存放在:
1.记忆体内存
2.cookie本身
3.redis或memcached等缓存中(常见)
4.数据库中ex. mongoDB。
这边,我将示范,在mongoDB 存session,很简单,只要3个步骤:
- 首先,必须先安装express-session及connect-mongo (还有更多session储存的API,可以参考 https://github.com/expressjs/session#compatible-session-stores))
- 安装好后,我们可以用简单的 计数器程式做例子!首先,引入module:
var session = require('express-session');
const MongoStore = require('connect-mongo')(session);
引入后,在session相关option设置,加入
store:new MongoStore({url:’mongodb://localhost:27017/sessiondb’})
如图:
如此,大功告成!!
在robo 3T查看数据是否增加
登陆
建模:express, express-session, body-parser,connect-mongo这些
在public下添加session文件夹,在添加login.html
index.js
//載入第三方模組
var express = require('express');
// 引入 express-session
var session = require('express-session');
// session store
const MongoStore = require('connect-mongo')(session);
var bodyParser = require('body-parser');
//载入路由档案
var routerSession=require('./routes/loginAPI');
var app = express(); //建立一个express物件
//set view engine
app.set("view engine","pug")
//set view directory
app.set("views",__dirname+"/views")
//將request进來的data 转成 json()
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
//設置session相關設定
app.use(session({
secret: 'recommand 128 bytes random string',
store:new MongoStore({url:'mongodb://tester:password@localhost:27017/lxkdb'}),
resave: false,
saveUninitialized: true,
cookie: { maxAge: 600 * 1000 } //10分鐘到期
}));
// Create a router to handle routes for a set of loginAPI
// 抓出來, 变成独立档案
// -------------------------------------------------------
//静态档案 like .js, .json, .xml, html....
app.use(express.static(__dirname+'/public'));
// 允许 /session 使用這個路由
app.use('/session', routerSession);
app.listen(3000,function(){
console.log('Ready...for 3000,二哈!!!');
});
loginAPI.js
var express = require("express");
var loginAPI =express.Router();
//进入需要验证的界面
var isLogin=false;
loginAPI.get('/',function(req,res){
// 一开始预设,所有的登入状态isLogin都是false,预设的登入者是guest。
//如果,我们接收到的session皆存在,则改变登入者姓名及登入状态。
//然而,无论有没有登入,皆会导到index.jade样版,去做呈现。
//这边与cookie的不同,多了一個測試 進入次數的計數呈現(非必要)
var name='guest';
isLogin=false;
var Logtime=1;
if(req.session.firstName && req.session.lastName){
name=req.session.firstName+ ' '+req.session.lastName;
isLogin = true;
Logtime= req.session.time;
}
res.render('index', { title: 'Express', member:name, logstatus:isLogin,time:Logtime });
});
loginAPI.post('/post', function(req, res) {
// 表单送出后,开始进行条件判断。
//如果fristName, lastName其中一个栏位没有填写,则再重回登入页。
//否则,再进行下一个条件判断:只要,两者输入,在session store已有(在未登出情况下,同一笔连线),表示session早已存在,登入次数加1,直接到需要验证的页面即可!– 否则,重新设置session,并导向验证页面。(這裡的判別,可以做省略,主要是為了測試store用!)
if(req.body.firstName=="" || req.body.lastName=="")
{
return res.redirect('Login.html');
}else if(req.body.firstName==req.session.firstName
&& req.body.lastName==req.session.lastName)
//如果輸入的,在session store已有儲存..
{
req.session.time++; //同一連線的登入次數, 就加 1
return res.redirect('/session'); //就直接導向到...
}
else
{
//session store裡沒有的,就會重新設置
req.session.firstName=req.body.firstName;
req.session.lastName=req.body.lastName;
req.session.time=1;
return res.redirect('/session');
}
});
// 登出...
loginAPI.get('/logout', function(req, res) {
//session清空!尔后,导向/session路由,
req.session.destroy();
return res.redirect('/session');
});
module.exports = loginAPI;
访问如图: