Nodejs etag session如何保存http会话
Nodejs etag session如何保存http会话
我们都知道,http的session是保存用户访问状态的,标识用户身份的重要的东西,但是大部分session都是基于浏览器的cookie,仅有很少一部分还在使用基于url的session机制。如果cookie被用户禁用,我们又不想使用安全性较差的url的session机制,是不是就没有办法了呢?最近在微博上看到一篇利用etag来作为sessionid保存http会话的文章颇有感触,当然我在此文章的基础上更深入一步,让这套etag的session架构似乎可以投入生产了。 项目代码地址:https://github.com/DoubleSpout/etagSession 大家可以从上面的github地址下载代码,运行
node app.js
然后访问127.0.0.1:3001/login/load/ 这个查看效果。
下面我们根据截图一起来领略下etag作为sessionid的妙用
1、首先我们打开地址:/login/load/ 进入登录跳转页面
2、因为我们第一次进入,所以跳转到表单页面,文本框内没有内容
3、填写表单内容并且提交表单
4、我们关闭浏览器,重新打开chrome,直接输入/login/index/ 这个url地址
5、显示出了刚才我们提交的信息了,信息已经同session一样保存在了服务端
7、打开127.0.0.1下面的cookie信息,发现没有任何使用cookie的痕迹
这个etag保存session的架构,我在ie6,7,8,9,10, chrome下均测试通过。
简单说一下流程吧: 1、用户进入 /login/load/进入跳转页,这个页面的关键是让用户先加载一次 /etag_session.js 这个文件,保证用户浏览器第二次再加载这个文件时会带上 if-none-match 这个请求头。
2、用户进入 /login/index/ 之后,会先加载 /etag_session.js 这个文件,然后服务端会根据用户请求的 if-none-match 响应相同的etag,然后响应这个js文件也会带上etag这个字符串。所以这个 /etag_session.js 内容是动态生成的,但是我们也要保证让浏览器每次都来请求这个js文件,所以我们的响应头得加上 ‘Cache-Control’ = ‘max-age=0’。 前端js会将 etag_session.js 返回的etag字符串保存在表单的隐藏文本域中,这样提交给nodejs之后,就将etag标识和用户的信息关联起来了。
3、用户关闭浏览器,重新打开地址之后,因为请求 /etag_session.js 这个地址的 if-none-match 的值没有变,所以动态生成的 /etag_session.js 文件的内容就可以根据用户请求的 if-none-match 的值来找到用户的信息返回出去了。前端js拿到用户信息就将信息写入文本框中
总结下吧,利用etag进行session存储优缺点都很明显: 缺点: 1、如果用户ctrl+f5,或者清空了缓存,用户就不得不重新登录了 2、万一网站被xss注入了,很容易就获取了用户的sessionId,如果基于cookie,可以使用httponly来避免js获取sessionid
优点: 1、如果cookie被禁用,etag方案似乎是比url方案更安全靠谱一些 2、如果存在多个站点需要做单点登录,合理利用etag方案可以简单不少,不用像以前我们通过url跳转刷cookie,而且碰到iframe的时候蛋疼的ie还不发cookie,需要加上p3p头。 利用etag机制之后,只需要加载一下 登录中心的 某一个js,如果用户在登录中心登录过,就可以轻松获取用户的基本信息了,但是处于安全考虑可能只会获取一些诸如头像,昵称,性别等信息。 所以如果合理的配合使用cookie和etag来保存http会话,可以让我们解决一些复杂的跨站点登录问题提供一个简单的思路
博客原文:http://snoopyxdy.blog.163.com/blog/static/6011744020137209451697/
hoho~图片重见天日了
楼主的文章都很有货,想拜师啊。
如果用户ctrl+f5,或者清空了缓存,用户就不得不重新登录了 这个是硬伤……
mark下 思路不错 不过多了个proxy和一次跳转 稍微复杂了一点点
弱弱的问一句,文中提到“在ie6,7,8,9,10, chrome下均测试通过”,那firefox有没有测试过?
要在Node.js中使用ETag来保存HTTP会话,我们可以利用HTTP的缓存机制和ETag头部。下面是实现这一机制的一个简化示例,展示了如何使用ETag来保存和恢复会话信息。
示例代码
首先,我们需要创建一个简单的Express应用,其中包含处理登录、验证和渲染页面的逻辑。
const express = require('express');
const crypto = require('crypto');
const app = express();
// 模拟的用户数据
let users = {
'12345': { username: 'testUser', email: 'test@example.com' }
};
app.use(express.json());
// 生成ETag
function generateETag(data) {
return crypto.createHash('sha256').update(JSON.stringify(data)).digest('hex');
}
app.get('/login/load', (req, res) => {
const etag = req.headers['if-none-match'];
let sessionId = req.cookies.sessionId || etag;
let userData = users[sessionId];
if (!userData && !etag) {
// 第一次访问,重定向到登录页面
res.redirect('/login/form');
} else {
// 根据ETag或Cookie中的sessionId查找用户数据
let newETag = generateETag(userData);
if (newETag === etag) {
// 如果ETag匹配,返回相同的内容
res.set('ETag', newETag);
res.send(`
<form id="loginForm">
<input type="hidden" name="etag" value="${newETag}">
<label>Email: <input type="text" name="email" value="${userData.email}"></label>
<button type="submit">Submit</button>
</form>
`);
} else {
// 如果ETag不匹配,返回新的数据
res.set('ETag', newETag);
res.send(`
<form id="loginForm">
<input type="hidden" name="etag" value="${newETag}">
<label>Email: <input type="text" name="email" value=""></label>
<button type="submit">Submit</button>
</form>
`);
}
}
});
app.post('/login', (req, res) => {
const { email } = req.body;
let sessionId = crypto.randomBytes(20).toString('hex');
users[sessionId] = { email };
res.cookie('sessionId', sessionId, { maxAge: 900000, httpOnly: true });
res.redirect(`/login/load`);
});
app.listen(3001, () => console.log('Server running on port 3001'));
解释
- ETag生成:
generateETag
函数用于根据用户数据生成一个唯一的ETag。 - GET请求处理:当用户访问
/login/load
时,服务器检查是否存在ETag(即用户是否已登录)。如果存在,则返回带有ETag的HTML表单;否则,重定向到登录表单页面。 - POST请求处理:当用户提交表单时,服务器创建一个新的会话ID并将其与用户数据关联起来。然后设置一个HTTP-only Cookie,确保客户端JavaScript无法访问它。
- 表单处理:表单中包含一个隐藏的
etag
字段,用于在客户端JavaScript中捕获ETag,并将其发送回服务器以保持会话状态。
这种方法允许我们在用户禁用Cookie的情况下仍然保持会话状态,但需要注意的是,ETag的安全性可能不如传统的Cookie+HTTPS组合,因此在生产环境中还需要额外的安全措施。