在nodejs中使用express和express-session管理会话
什么是session
web是基于 HTTP 协议的, 而HTTP 是一种无状态协议,也就是说在每个请求和响应结束时,客户端和服务器不知道对方是谁。
这就是session的用处,session也叫做会话。允许服务器在多个请求之间维护用户的数据和状态,从而实现用户身份验证、保持登录状态、存储用户信息等功能。
session的工作机制
- 客户端(浏览器)发送首次请求到服务器。
- 服务器收到请求,发现客户端没有提供会话ID(缺少一个值为
session-id
的cookie)。 - 服务器创建一个新的会话,生成一个唯一的会话ID,并将其存储在
session-id
cookie 中,并将会话数据保存在后端存储中(例如内存、数据库等)。 - 服务器将会话ID作为响应的
Set-Cookie
头部发送回客户端,客户端浏览器会存储这个会话ID。 - 从此以后,客户端的每个请求都会附带这个会话ID的
session-id
cookie。 - 服务器根据收到的会话ID,从后端存储中查找相应的会话数据,以恢复用户的会话状态。
session和cookie的区别
cookie 是存储在浏览器中的键值对。浏览器会吧对应域名下面的 cookie 附加到发送到服务器的每个 HTTP 请求。
在 cookie 中,无法存储大量数据,cookie的大小通常受到浏览器对单个cookie大小和每个域名的cookie总数的限制,一般较小。最好不要吧敏感的数据比如用户信息存在cookie中。
session数据存储在服务器端,一般会放在数据库里面。因此,它可以容纳更大量的数据。session是通过在客户端cookie中存储会话ID,服务器使用这个ID查找对应的会话数据来实现。
开发环境设置
初始化package.json
$ npm init –y
安装express-session
在nodejs中管理session(会话),需要使用express-session这个中间件,可以使用下面的命令安装这个模块:
$ npm i express-session
安装express
要在 Node.js 中使用 express-session 模块设置会话,还需要安装 Express 模块:
$ npm i express
使用express-session
const express = require('express')const session = require('express-session')const app = express()
app.use(session({ secret: 'Your_Secret_Key', resave: false,}))app.get('/',(req,res) => { res.send("hello");});app.listen(3000, () => { console.log('Server is running at port 3000')})
使用app.use(session(options))
就可以使用这个中间件,其中:
secret
是一个密钥,用来签名**session-id
**
resave
bool类型,默认为true,用于指定是否在每次请求时重新保存会话,即使会话在请求过程中从未被修改过。比如客户端向服务器发出两个并行请求,当第二请求结束时,对第一请求的会话所做的修改可能会被覆盖。
执行$node app.js
之后,我们使用curl命令来测试这个程序
$ curl -i localhost:3000
设置cookie的属性
上面例子可以看到session中间件帮我们设置了一个cookie,cookie的默认key为:connect.sid,
可以使用name字段设置cookie的key
app.use(session({ secret: 'Your_Secret_Key', resave: false, name: 'mycookie'}))
还可以配置cookie的其他属性,如maxAge,secure等
//过期时间为一天const oneDay = 1000 * 60 * 60 * 24;app.use(session({ secret: 'Your_Secret_Key', resave: false, cookie: { maxAge: oneDay, HttpOnly: true, secure: false}, name: 'mycookie'}))
使用session记录页面访问次数
上面的例子没有什么用处,这里我们来实现一个记录用户访问页面次数的例子:
app.get('/', function (req, res, next) { if (req.session.views) { req.session.views++ res.send('views: ' + req.session.views) } else { req.session.views = 1 res.end('welcome to the session demo. please curl again!') }})
当客户端第一次请求时,我们会给req.session写入一个views属性,当客户端再次发起请求时,会吧views加1,然后返回给客户端
使用curl请求:
$ curl -i localhost:3000
可以看到,响应的是’welcome to the session demo. please curl again!’
使用curl携带上一次的cookie再次请求:
$ curl -i -b "mycookie=s%3A_lsIhF-wH-IwuiUBeKcpqq_KIv1JVfFW.PVY28Bj0DhABROPOZM9h%2BY4gcbb7MVEyYqMu8lH73LI" localhost:3000
再次请求之后会响应用户访问的次数
使用session实现登陆退出功能
实现登录页面
登录页面是这样的:
login.html代码如下:
<!DOCTYPE html><html><head> <title>Login</title> <style> body { font-family: Arial, sans-serif; background-color: #f2f2f2; margin: 0; padding: 0; display: flex; justify-content: center; align-items: center; height: 100vh; }
.login-container { background-color: #fff; border-radius: 5px; padding: 20px; box-shadow: 0 0 10px rgba(0, 0, 0, 0.1); width: 300px; }
.login-container h1 { text-align: center; margin-bottom: 20px; }
.login-form label { display: block; margin-bottom: 10px; }
.login-form input[type="text"], .login-form input[type="password"] { width: 100%; border: 1px solid #ccc; border-radius: 3px; }
.login-form input[type="submit"] { background-color: #007bff; color: #fff; border: none; padding: 10px; border-radius: 3px; width: 100%; cursor: pointer; }
.login-form input[type="submit"]:hover { background-color: #0056b3; } </style></head><body> <div class="login-container"> <h1>Login Form</h1> <form class="login-form" method="POST" action="/login"> <label for="username">Username:</label> <input type="text" id="username" name="username" required><br><br>
<label for="password">Password:</label> <input type="password" id="password" name="password" required><br><br>
<input type="submit" value="Login"> </form> </div></body></html>
这个HTML登录表单,会使用POST方法将用户名和密码提交到服务器上的/login路由
处理根路由请求
app.get('/', function (req, res, next) { if (req.session.userid) { res.send("欢迎 <a href=\'/logout'>点击退出</a>"); } else { res.sendFile('./login.html', { root: __dirname }) }})
跟路由请求会判断session里面是否有userid,如果有,则显示退出,否则显示登录页面
处理/login登录请求
//使用urlencoded中间件解析post请求app.use(express.urlencoded());
const myusername = 'admin';const mypassword = 'admin';app.post('/login', (req, res) => { if (req.body.username === myusername && req.body.password === mypassword) { **req.session.userid = req.body.username;** console.log(req.session) res.send(`登录成功 <a href=\'/logout'>点击退出</a>`); } else { res.send('不合理的 username 或 password'); }})
这里我们用到了express.urlencoded中间件,这个中间件可以帮我们解析POST请求,并吧请求体的字段放在req.body里面
然后我们处理login请求,为了演示我们吧用户名和密码写死,用户名密码匹配之后,我们会吧username写入session
运行一下程序:
node app.js
输入用户名和密码登录之后可以看到登录成功,并且有了cookie,再次刷新页面,还会显示登录成功的页面,因为用户的信息已经被放在session里面
然后会到命令行可以看到打印的session如下:
实现/logout退出接口
app.get('/logout',(req,res) => { req.session.destroy(); res.redirect('/');});
我们直接调用session的destroy方法就可以实现退出
res.redirect是重定向功能,通过它会向用户返回一个 302 状态,通知 浏览器转向相应页面。
点击退出登录,我们就会回到首页,并且显示了登录页面
问题
可以看出登入和登出仅仅是 req.session.userid 变量的标记,非常简单。但这会不会有安全性问题呢?是不会的,因为这个变量只有服务端才能访问到,只要不是黑客攻破了整个服务器,无法从外部改动
完整代码地址:
https://github.com/AC-greener/nodejs-session
总结
这篇文章我们学会了使用session来管理会话,但是还有一些不足,我们的session是存在服务器内存的,当重启服务器时,session就没了,或着当有多台服务器时,会话状态也会出现不同步,所以一般会使用MongoDB或者Redis来存储会话,下一篇文章我们会使用到MongoDB~