cookie
在网站中,http请求是无状态的。也就是说即使第一次和服务器连接后并且登录成功后,第二次请求服务器依然不能知道当前请求是哪个用户。cookie的出现就是为了解决这个问题,第一次登录后服务器返回一些数据(cookie)给浏览器,然后浏览器保存在本地(浏览器存
),当该用户发送第二次请求的时候,就会自动的把上次请求存储的cookie数据自动的携带给服务器,服务器通过浏览器携带的数据就能判断当前用户是哪个了。cookie存储的数据量有限,不同的浏览器有不同的存储大小
,但一般不超过4KB。因此使用cookie只能存储一些小量的数据。
使用示例
//steps-01:安装依赖
cnpm install cookie-parser
//steps-02:建服务器
//cookie-server.js
var express = require('express');
var cookie = require('cookie-parser');
var router = express.Router();
outer.get('/', function (req, res, next) {
//设置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: '首页'});
});
app.listen(3000);
http://wiki.jikexueyuan.com/project/node-lessons/cookie-session.html
session
用户登录成功,服务端会创建和保存一个session(服务器存
),并给客户端一个sessionId。客户端会把sessionId保存在cookie中,每次请求都会携带这个sessionId。
//steps-01:创建项目
express -e
//steps-02:安装依赖
npm install
npm install --save express-session session-file-store
//steps-03:建服务器
//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', // 用来对session id相关的cookie进行签名
store: new FileStore(), // 本地存储session(文本文件,也可以选择其他store,比如redis的)
saveUninitialized: false, // 是否自动保存未初始化的会话,建议false
resave: false, // 是否每次都重新保存会话,建议false
cookie: {
maxAge: 10 * 1000 // 有效期,单位是毫秒
}
}));
//steps-04:账户数据
//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: '账号或密码错误'});
}
});
//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:判登陆态
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:启客户端
存在哪里
其中,开发环境存内存就好了。一般的小程序为了省事
,如果不涉及状态共享的问题,用内存 session 也没问题。但内存 session 除了省事之外,没有别的好处。
用 cookie session 的话,是不用担心状态共享
问题的,因为 session 的 data 不是由服务器来保存,而是保存在用户浏览器端,每次用户访问时,都会主动带上他自己的信息。当然在这里,安全性之类的,只要遵照最佳实践来,也是有保证的。它的弊端是增大了数据量传输,利端是方便。
缓存方式是最常用的方式了,即快,又能共享状态。相比 cookie session 来说,当 session data 比较大的时候,可以节省网络传输
。推荐使用。
数据库 session。除非你很熟悉这一块,知道自己要什么,否则还是老老实实用缓存吧。
session 的 store 有四个常用选项:1)内存 2)cookie 3)缓存 4)数据库
cookie+session这种模式通常是保存在内存中,而且服务从单服务到多服务会面临的session共享问题,随着用户量的增多,开销就会越大。而JWT不是这样的,只需要服务端生成token,客户端保存这个token,每次请求携带这个token,服务端认证解析就可。
JWT(JSON WEB Token)
一种基于JSON的、用于在网络上声明某种主张的令牌(token)。JWT通常由三部分组成: 头信息(header), 消息体(payload)和签名(signature)。
头信息指定了该JWT使用的签名算法
实现示例
//steps-01:创建头部
{
//类型
"typ": "JWT",
//算法
"alg": "HS256"
}
//转成字符:base64
//steps-02:创建荷载
//标准部分(建议但不强制使用)
iss: jwt签发者
sub: jwt发送方
aud: jwt接收方
iat: jwt的签发时间
exp: jwt的过期时间
nbf: jwt的可用起点
jti: jwt的唯一标识
//公共部分
//私有部分
{
"name":"Free码农",
"age":"28",
"org":"今日头条"
}
//转成字符:base64
//steps-03:创建签证
//头部
//荷载
//密码
使用示例
//安装依赖
{"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': '数据库链接或者其他'
};
//数据文件
//user.js
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
module.exports = mongoose.model('User', new Schema({
name: String,
password: String,
admin: Boolean
}));
//建服务器
/*
技术注释://
业务注释://-
*/
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();
//连数据库
mongoose.connect(config.database);
//设全局量
app.set('superSecret', config.jwtsecret);
//使用插件
//析请求体
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
//访问日志
app.use(morgan('dev'));
//路径处理
app.get('/', function(req, res) {
res.send('JWT 授权访问的API路径 http://localhost:' + config.network.port + '/api');
});
//存数据库
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('用户存储成功');
res.json({ success: true });
});}
else{
res.json({ success: false,msg:"错误参数" });
}
});
//获取令牌
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: '用户账号错误' });
} else if (user) {
if (user.password != req.body.password) {
res.json({ success: false, message: '用户密码错误' });
} else {
var token = jwt.sign(user, app.get('superSecret'), {
expiresIn : 60*60*24// 授权时效24小时
});
res.json({
success: true,
message: '请使用您的授权码',
token: token
});
}
}
});
});
//接口监听
app.listen(config.network.port);
console.log('JWT测试服务已经开启地址: http://localhost:' + config.network.port);
适用场景
JWT适合一次性
的命令认证,颁发一个有效期极短
的JWT,即使暴露了危险也很小,由于每次操作都会生成新的JWT,因此也没必要保存JWT,真正实现无状态。