后台开发之cookie、session详解
cookie和session,为浏览器和web服务器之间**维持状态(登录态、重要信息等)**提供了有效的途径,是每个后台开发必须要掌握的技能。
先说说为什么需要cookie和session,大家都知道http协议是无状态的,那么怎么在浏览器和服务器之间保持状态呢?
比如只有你登录了某购物网站,你才能看到自己的用户信息、购物车信息、订单信息,不能每次打开购物车你都需要重新登录一下,那用户不是烦死了。所以,当你登录一次之后,服务器端就种下cookie返回给浏览器,浏览器以后每次访问服务器都会带上这个cookie(浏览器行为),这样一来服务器解析这个cookie后就知道是哪个用户了。
下面先介绍一下概念,接着代码实现一下,光说不练假把式。
概念介绍
-
cookie:信息保存在浏览器端。当浏览器第一次访问时,服务器种下cookie返回给浏览器,以后每次访问时浏览器都会带上这个cookie。cookie有大小(单个cookie保存的数据不能超过5 K)和数量的限制(很多浏览器都限制一个站点最多保存20个cookie)。
-
Session:信息保存在服务器端,是基于cookie传输的。当浏览器第一次访问时,服务器创建session的同时将sessionId保存到cookie中返回给浏览器,以后每次访问浏览器都带上含有这个sessionId的cookie,服务器拿到这个sessionId,在内存或redis中解析出信息。
问题分析
为什么有了cookie还需要session?
cookie是将信息存储浏览器端的,很容易被篡改或伪造,而session是将sessionId返给浏览器,仅仅能看到的是一串很长的乱码,除非绝顶高手很难破解。
让我们假设一种场景,用户A登录后,进入首页,显示“欢迎A访问首页”。
此时,我们简单的伪造一下username,然后再访问这个页面。
可以看到很容易就被伪造了信息。
应该怎么解决这个问题呢?
解决方案:
- 签名,通过签名来增加安全性。利用加密算法hmac(username, secret) -> ‘xxxxxxxxx’,即Cookie: uername=username|xxxxxxxxx,这样即使修改了uername,但签名对不上,可以判定是伪造的信息。
-
cookie中存放sessionId,server端对应username -> 即session
Cookie:sessionId=s%3Aa4hag_CW42vcykKBdxxxxxx,在服务器端存储sessionId所对应的数据,即使你想篡改sessionId也不知道从何改起,很大程度上保证了安全性。
代码实现
一、签名
- 自己实现签名过程
const express = require('express');
const cookieParser = require('cookie-parser');
const crypto = require('crypto');
const Hmac = (val, secret) => {
const hmac = crypto.createHmac('sha256', secret)
.update(val)
.digest('Base64');
return hmac;
}
const app = express();
app.use(cookieParser());
let secret = 'abcd';
app.get('/login', (req, res) => {
const hmac = Hmac(req.query.username, secret); // 生成签名
res.cookie(`username`, `${req.query.username}|${hmac}`, {signed: false});
res.send('登录成功');
});
app.get('/pageOne', (req, res) => {
console.log('Cookies:', req.cookies); // 普通cookie
if (req.cookies.username) {
let usernameArr = req.cookies.username.split('|');
console.log('usernameArr=', usernameArr);
const hmac = Hmac(usernameArr[0], secret); // 签名校验
if (hmac !== usernameArr[1]) {
res.send('您的cookie是伪造的');
return;
}
res.send(`欢迎${req.cookies.username}访问首页`);
} else {
res.send('您还未登录,请先登录')
}
});
app.listen(8002, () => {
console.log('Server start. port=', 8002);
});
- 利用cookie-parser中间件,该中间件的实现原理与上面类似,只是做了一层封装
const express = require('express');
const cookieParser = require('cookie-parser');
const app = express();
app.use(cookieParser('abcd'));
app.get('/login', (req, res) => {
res.cookie(`id`, 1234, {signed: false});
res.cookie(`username`, `${req.query.username}`, {signed: true});
res.send('登录成功');
})
app.get('/pageOne', (req, res) => {
console.log('Cookies:', req.cookies); // 普通cookie,Cookies: { id: '1234' }
console.log('Signed Cookies:', req.signedCookies); // 带签名的cookie,{ username: 'A' }
if (req.signedCookies.username) {
res.send(`欢迎${req.signedCookies.username}访问首页`);
} else {
res.send('您还未登录,请先登录')
}
})
app.listen(8002, () => {
console.log('Server start. port=', 8002);
})
二、session
- 自己实现,实现过程为在服务器端生成sessionId,同时将对应的信息存储下来,可以放在内存中也可以放在redis中,原理大概就是这样,这里实现略了。
- 利用express-session中间件
const express = require('express');
const cookieParser = require('cookie-parser');
const session = require('express-session');
// redis 模块
var redis = require('redis');
var client = redis.createClient('6379', '127.0.0.1');// 默认监听6379端口,'127.0.0.1'为你本地ip(默认不需要修改)
var redisStore = require('connect-redis')(session);
const app = express();
app.use(cookieParser());
// redis 链接错误
client.on("error", function(error) {
console.log('redis error:', error);
});
app.use(session({ // 设置cookie,返回浏览器的是connect.sid(默认)=s%3A4o9vyxxxxxx
name: 'sessionId',
secret: 'abcd', // secret的值建议使用随机字符串
rolling: true, // true,到期时间会被重置为原始的maxAge
saveUninitialized: false, // false,未初始化的session的cookie将不会在response中设置
cookie: {
maxAge: 6 * 60 * 1000 // 过期时间(毫秒)
},
store: new redisStore() // (使用redis的存储session)
}));
app.get('/login', (req, res) => {
res.cookie(`id`, 1234);
req.session.username = req.query.username;
console.log('login cookies:', req.cookies);
console.log('login session:', req.session);
res.send('登录成功');
});
app.get('/pageOne', (req, res) => {
console.log('pageOne cookies:', req.cookies);
console.log('pageOne session:', req.session);
if (req.session.username) {
res.send(`欢迎${req.session.username}访问首页`);
} else {
res.send('您还未登录,请先登录')
}
});
app.listen(8002, () => {
console.log('Server start. port=', 8002);
})
这里将数据存储到redis中。需要注意的是,sessionId对应的数据存储在redis中,通过req.session可以获取到所有数据。
结语
cookie和session多用于登录校验,在实际项目中,通常将登录信息等重要信息存放为session、其他信息如果需要保留,可以放在cookie中。
到这里基本已经介绍完了,只要你敲一遍上面的代码,我相信你肯定能够掌握cookie和session的概念以及怎么灵活地使用它们。