cookie
在網(wǎng)站中,http請(qǐng)求是無(wú)狀態(tài)的向拆。也就是說(shuō)即使第一次和服務(wù)器連接后并且登錄成功后亚茬,第二次請(qǐng)求服務(wù)器依然不能知道當(dāng)前請(qǐng)求是哪個(gè)用戶。cookie的出現(xiàn)就是為了解決這個(gè)問(wèn)題亲铡,第一次登錄后服務(wù)器返回一些數(shù)據(jù)(cookie)給瀏覽器才写,然后瀏覽器保存在本地(瀏覽器存
)葡兑,當(dāng)該用戶發(fā)送第二次請(qǐng)求的時(shí)候奖蔓,就會(huì)自動(dòng)的把上次請(qǐng)求存儲(chǔ)的cookie數(shù)據(jù)自動(dòng)的攜帶給服務(wù)器,服務(wù)器通過(guò)瀏覽器攜帶的數(shù)據(jù)就能判斷當(dāng)前用戶是哪個(gè)了讹堤。cookie存儲(chǔ)的數(shù)據(jù)量有限吆鹤,不同的瀏覽器有不同的存儲(chǔ)大小
,但一般不超過(guò)4KB洲守。因此使用cookie只能存儲(chǔ)一些小量的數(shù)據(jù)疑务。
使用示例
//steps-01:安裝依賴
cnpm install cookie-parser
//steps-02:建服務(wù)器
//cookie-server.js
var express = require('express');
var cookie = require('cookie-parser');
var router = express.Router();
outer.get('/', function (req, res, next) {
//設(shè)置cookie
res.cookie('username','zhangsan');
res.cookie('username','zhangsan',{maxAge:10000}); //有效期以毫秒為單位
//獲取cookie
console.log(req.cookies);
console.log(req.cookies.username);
//刪除cookie
res.clearCookie('username');
res.render('/index', {title: '首頁(yè)'});
});
app.listen(3000);
http://wiki.jikexueyuan.com/project/node-lessons/cookie-session.html
session
用戶登錄成功,服務(wù)端會(huì)創(chuàng)建和保存一個(gè)session(服務(wù)器存
)梗醇,并給客戶端一個(gè)sessionId知允。客戶端會(huì)把sessionId保存在cookie中叙谨,每次請(qǐng)求都會(huì)攜帶這個(gè)sessionId温鸽。
//steps-01:創(chuàng)建項(xiàng)目
express -e
//steps-02:安裝依賴
npm install
npm install --save express-session session-file-store
//steps-03:建服務(wù)器
//session-server.js
var express = require('express');
var app = express();
var session = require('express-session');
var FileStore = require('session-file-store')(session);
var identityKey = 'skey';
app.use(session({
name: identityKey,
secret: 'chyingp', // 用來(lái)對(duì)session id相關(guān)的cookie進(jìn)行簽名
store: new FileStore(), // 本地存儲(chǔ)session(文本文件,也可以選擇其他store,比如redis的)
saveUninitialized: false, // 是否自動(dòng)保存未初始化的會(huì)話涤垫,建議false
resave: false, // 是否每次都重新保存會(huì)話姑尺,建議false
cookie: {
maxAge: 10 * 1000 // 有效期,單位是毫秒
}
}));
//steps-04:賬戶數(shù)據(jù)
//users.js
module.exports = {
items: [
{name: 'chyingp', password: '123456'}
]
};
//steps-05:登陸接口
//session-server.js
var users = require('./users').items;
var findUser = function(name, password){
return users.find(function(item){
return item.name === name && item.password === password;
});
};
// 登錄接口
app.post('/login', function(req, res, next){
var sess = req.session;
var user = findUser(req.body.name, req.body.password);
if(user){
req.session.regenerate(function(err) {
if(err){
return res.json({ret_code: 2, ret_msg: '登錄失敗'});
}
req.session.loginUser = user.name;
res.json({ret_code: 0, ret_msg: '登錄成功'});
});
}else{
res.json({ret_code: 1, ret_msg: '賬號(hào)或密碼錯(cuò)誤'});
}
});
//steps-05:退出接口
//session-server.js
app.get('/logout', function(req, res, next){
req.session.destroy(function(err) {
if(err){
res.json({ret_code: 2, ret_msg: '退出登錄失敗'});
return;
}
// req.session.loginUser = null;
res.clearCookie(identityKey);
res.redirect('/');
});
});
//steps-06:判登陸態(tài)
app.get('/', function(req, res, next){
var sess = req.session;
var loginUser = sess.loginUser;
var isLogined = !!loginUser;
res.render('index', {
isLogined: isLogined,
name: loginUser || ''
});
});
//steps-07:建客戶端
//steps-08:啟客戶端
存在哪里
其中蝠猬,開(kāi)發(fā)環(huán)境存內(nèi)存就好了切蟋。一般的小程序?yàn)榱?code>省事,如果不涉及狀態(tài)共享的問(wèn)題榆芦,用內(nèi)存 session 也沒(méi)問(wèn)題柄粹。但內(nèi)存 session 除了省事之外,沒(méi)有別的好處歧杏。
用 cookie session 的話镰惦,是不用擔(dān)心狀態(tài)共享
問(wèn)題的,因?yàn)?session 的 data 不是由服務(wù)器來(lái)保存犬绒,而是保存在用戶瀏覽器端旺入,每次用戶訪問(wèn)時(shí),都會(huì)主動(dòng)帶上他自己的信息凯力。當(dāng)然在這里茵瘾,安全性之類的,只要遵照最佳實(shí)踐來(lái)咐鹤,也是有保證的拗秘。它的弊端是增大了數(shù)據(jù)量傳輸,利端是方便祈惶。
緩存方式是最常用的方式了雕旨,即快,又能共享狀態(tài)捧请。相比 cookie session 來(lái)說(shuō)凡涩,當(dāng) session data 比較大的時(shí)候,可以節(jié)省網(wǎng)絡(luò)傳輸
疹蛉。推薦使用活箕。
數(shù)據(jù)庫(kù) session。除非你很熟悉這一塊可款,知道自己要什么育韩,否則還是老老實(shí)實(shí)用緩存吧。
session 的 store 有四個(gè)常用選項(xiàng):1)內(nèi)存 2)cookie 3)緩存 4)數(shù)據(jù)庫(kù)
cookie+session這種模式通常是保存在內(nèi)存中闺鲸,而且服務(wù)從單服務(wù)到多服務(wù)會(huì)面臨的session共享問(wèn)題筋讨,隨著用戶量的增多,開(kāi)銷就會(huì)越大摸恍。而JWT不是這樣的悉罕,只需要服務(wù)端生成token,客戶端保存這個(gè)token,每次請(qǐng)求攜帶這個(gè)token蛮粮,服務(wù)端認(rèn)證解析就可益缎。
JWT(JSON WEB Token)
一種基于JSON的、用于在網(wǎng)絡(luò)上聲明某種主張的令牌(token)然想。JWT通常由三部分組成: 頭信息(header), 消息體(payload)和簽名(signature)莺奔。
頭信息指定了該JWT使用的簽名算法
實(shí)現(xiàn)示例
//steps-01:創(chuàng)建頭部
{
//類型
"typ": "JWT",
//算法
"alg": "HS256"
}
//轉(zhuǎn)成字符:base64
//steps-02:創(chuàng)建荷載
//標(biāo)準(zhǔn)部分(建議但不強(qiáng)制使用)
iss: jwt簽發(fā)者
sub: jwt發(fā)送方
aud: jwt接收方
iat: jwt的簽發(fā)時(shí)間
exp: jwt的過(guò)期時(shí)間
nbf: jwt的可用起點(diǎn)
jti: jwt的唯一標(biāo)識(shí)
//公共部分
//私有部分
{
"name":"Free碼農(nóng)",
"age":"28",
"org":"今日頭條"
}
//轉(zhuǎn)成字符:base64
//steps-03:創(chuàng)建簽證
//頭部
//荷載
//密碼
使用示例
//安裝依賴
{"dependencies": {
"body-parser": "^1.17.2",
"express": "^4.15.3",
"express-jwt": "^5.3.0",
"jsonwebtoken": "^7.4.1",
"mongoose": "^4.10.4",
"morgan": "^1.8.2"
}}
//配置文件
//config.js
module.exports = {
'network' : {
'port':8080
},
'jwtsecret': 'myjwttest',
'database': '數(shù)據(jù)庫(kù)鏈接或者其他'
};
//數(shù)據(jù)文件
//user.js
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
module.exports = mongoose.model('User', new Schema({
name: String,
password: String,
admin: Boolean
}));
//建服務(wù)器
/*
技術(shù)注釋://
業(yè)務(wù)注釋://-
*/
var express = require('express');
var bodyParser = require('body-parser');
var morgan = require('morgan');
var mongoose = require('mongoose');
var jwt = require('jsonwebtoken');
var config = require('./config');
var User = require('./user');
var app = express();
//連數(shù)據(jù)庫(kù)
mongoose.connect(config.database);
//設(shè)全局量
app.set('superSecret', config.jwtsecret);
//使用插件
//析請(qǐng)求體
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
//訪問(wèn)日志
app.use(morgan('dev'));
//路徑處理
app.get('/', function(req, res) {
res.send('JWT 授權(quán)訪問(wèn)的API路徑 http://localhost:' + config.network.port + '/api');
});
//存數(shù)據(jù)庫(kù)
app.post('/setup', function(req, res) {
if(req.body.name && req.body.password){
var nick = new User({
name: req.body.name,
password: req.body.password,
admin:req.body.admin||false
});
nick.save(function(err) {
if (err) throw err;
console.log('用戶存儲(chǔ)成功');
res.json({ success: true });
});}
else{
res.json({ success: false,msg:"錯(cuò)誤參數(shù)" });
}
});
//獲取令牌
app.post('/authenticate', function(req, res) {
User.findOne({
name: req.body.name
}, function(err, user) {
if (err) throw err;
if (!user) {
res.json({ success: false, message: '用戶賬號(hào)錯(cuò)誤' });
} else if (user) {
if (user.password != req.body.password) {
res.json({ success: false, message: '用戶密碼錯(cuò)誤' });
} else {
var token = jwt.sign(user, app.get('superSecret'), {
expiresIn : 60*60*24// 授權(quán)時(shí)效24小時(shí)
});
res.json({
success: true,
message: '請(qǐng)使用您的授權(quán)碼',
token: token
});
}
}
});
});
//接口監(jiān)聽(tīng)
app.listen(config.network.port);
console.log('JWT測(cè)試服務(wù)已經(jīng)開(kāi)啟地址: http://localhost:' + config.network.port);
適用場(chǎng)景
JWT適合一次性
的命令認(rèn)證,頒發(fā)一個(gè)有效期極短
的JWT变泄,即使暴露了危險(xiǎn)也很小令哟,由于每次操作都會(huì)生成新的JWT,因此也沒(méi)必要保存JWT妨蛹,真正實(shí)現(xiàn)無(wú)狀態(tài)屏富。