也是用来 解决 http 无状态协议的问题(无法区分多次请求是否发送自同一客户端)
npm install express-session
npm install connect-mongo
const session = require(‘express-session‘); const MongoStore = require(‘connect-mongo‘)(session);
app.use(express.session({
secret: ‘keyboard cat‘, // 加密字符串,参与 sessionid 加密
saveUninitialized: false, // 在存储某东东之前,不会创建 session 对象
resave: false, // 如果没有修改 session 对象,就不会重新保存
store: new MongoStore({
url: ‘mongodb://localhost: 27017/test-app‘, // 连接数据库的地址
touchAfter: 24 * 3600 // 24 小时更新一次
})
}));
...
// 设置 session 会在数据库中创建 session 对象
// 保存 userId=findRet.id 到数据库
request.session.userId = findRet.id;
...
// 解析 cookie 中的 session 去数据库中找对应 sessionId 的数据
// 返回一个 cookie
const {userId} = request.session;
...
读写二合一
存储数据近乎无限大,取决于 服务器 的存储容量
传输流量小(数据传输过程中 cookie 更小更少)
app.use(session({... ...});
登录/注册 实例
package.json
{
"name": "node_express",
"version": "1.0.0",
"main": "index.js",
"license": "MIT",
"dependencies": {
"connect-mongo": "^2.0.3",
"cookie-parser": "^1.4.3",
"ejs": "^2.6.1",
"express": "^4.16.4",
"express-session": "^1.15.6",
"mongoose": "^5.4.0",
"sha1": "^1.1.1"
}
}
public/login.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8"/>
<title>用户登录</title>
<link rel="stylesheet" type="text/css" href="css/index.css"/>
</head>
<body>
<div id="outer_box" class="login">
<h2>用户登录</h2>
<form action="http://localhost:3000/login" method="post">
<div class="clothes">
<label for="input_name">用 户 名</label>
<input id="input_name" type="text" name="user_name" placeholder="请输入用户名" />
</div>
<div class="clothes">
<label for="input_pwd">密 码</label>
<input id="input_pwd" type="password" name="user_pwd" placeholder="请输入密码" />
</div>
<div class="clothes">
<a class="btn" href="http://localhost:3000/register">
<button type="button">注册</button>
</a>
<button class="login btn" type="submit">登录</button>
</div>
</form>
</div>
</body>
</html>
public/register.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8"/>
<title>用户注册</title>
<link rel="stylesheet" type="text/css" href="css/index.css"/>
</head>
<body>
<div id="outer_box" class="register">
<h2>用户注册</h2>
<form action="http://localhost:3000/register" method="post">
<div class="clothes">
<label for="input_name">用 户 名</label>
<input id="input_name" type="text" name="user_name" placeholder="请输入用户名" />
</div>
<div class="clothes">
<label for="input_pwd">密 码</label>
<input id="input_pwd" type="password" name="user_pwd" placeholder="请输入密码" />
</div>
<div class="clothes">
<label for="input_repeat_pwd">确认密码</label>
<input id="input_repeat_pwd" type="password" name="user_repeat_pwd" placeholder="请再次输入密码" />
</div>
<div class="clothes">
<label for="input_email">注册邮箱</label>
<input id="input_email" type="text" name="user_email" placeholder="请输入邮箱地址" />
</div>
<div class="clothes">
<button class="register btn" type="submit">注册</button>
<a class="btn" href="http://localhost:3000/login">
<button type="button">登录</button>
</a>
</div>
</form>
</div>
</body>
</html>
public/user_center.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8"/>
<title>用户中心</title>
<link rel="stylesheet" type="text/css" href="css/index.css"/>
</head>
<body>
<div id="outer_box" class="login">
<h2>个人空间</h2>
</div>
</body>
</html>
public/index.css
body {
width: 100%;
height: 100%;
color: #000;
background: #b9c2a4;
background-size: cover; /* 指定背景图片大小 */
}
/*************************************************/
#outer_box {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
color: #1a45c3;
}
#outer_box.login {
color: #9e098b;
}
#outer_box.register {
color: #1a45c3;
}
#outer_box>h2{
padding-bottom: 40px;
margin-left: -50px;
}
.clothes {
position: relative;
width: 260px;
display: flex;
justify-content: space-between;
margin: 20px 0;
font-size: 18px;
line-height: 32px;
}
.tips {
position: absolute;
top: 0;
right: -100%;
margin-left: -50%;
margin-top: 2px;
width: 252px;
height: 32px;
text-align: center;
color: #f00;
}
.clothes>label{
width: 80px;
text-align: center;
}
.clothes>input{
width: 170px;
height: 32px;
}
button {
width: 100%;
height: 100%;
font-size: 16px;
background-color: #c4ceda;
cursor: pointer;
}
.clothes .btn{
width: 64px;
height: 32px;
margin: 0 20px;
}
.clothes button.register{
background-color: #1a45c3;
color: #fff;
}
.clothes button.login{
background-color: #9e098b;
color: #fff;
}
#outer_box>h2 {
position: relative;
}
#server_info {
display: block;
width: 139px;
height: 20px;
border-radius: 10px;
position: absolute;
top: 0;
right: 0;
color: red;
font-size: 14px;
text-align: center;
line-height: 20px;
background-color: #cecece;
}
views/login.ejs
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8"/>
<title>用户登录</title>
<link rel="stylesheet" type="text/css" href="css/index.css"/>
</head>
<body>
<div id="outer_box" class="login">
<h2>
用户登录
<div id="server_info">
<%= serverInfo.tips %>
</div>
</h2>
<form action="http://localhost:3000/login" method="post">
<div class="clothes">
<label for="input_name">用 户 名</label>
<input id="input_name" type="text" name="user_name" value="<%= serverInfo.uName %>" placeholder="请输入用户名" />
</div>
<div class="clothes">
<label for="input_pwd">密 码</label>
<input id="input_pwd" type="password" name="user_pwd" placeholder="请输入密码" />
</div>
<div class="clothes">
<a class="btn" href="http://localhost:3000/register">
<button type="button">注册</button>
</a>
<button class="login btn" type="submit">登录</button>
</div>
</form>
</div>
</body>
</html>
views/register.ejs
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8"/>
<title>用户注册</title>
<link rel="stylesheet" type="text/css" href="css/index.css"/>
</head>
<body>
<div id="outer_box" class="register">
<h2>用户注册</h2>
<form action="http://localhost:3000/register" method="post">
<div class="clothes">
<label for="input_name">用 户 名</label>
<input id="input_name" type="text" name="user_name" value="<%= serverInfo.uName %>" placeholder="请输入用户名" />
<div class="tips"><%= serverInfo.nameErr %></div>
</div>
<div class="clothes">
<label for="input_pwd">密 码</label>
<input id="input_pwd" type="password" name="user_pwd" placeholder="请输入密码" />
<div class="tips"><%= serverInfo.passwordErr %></div>
</div>
<div class="clothes">
<label for="input_repeat_pwd">确认密码</label>
<input id="input_repeat_pwd" type="password" name="user_repeat_pwd" placeholder="请再次输入密码" />
<div class="tips"><%= serverInfo.repeatErr %></div>
</div>
<div class="clothes">
<label for="input_email">注册邮箱</label>
<input id="input_email" type="text" name="user_email" value="<%= serverInfo.uEmail %>" placeholder="请输入邮箱地址" />
<div class="tips"><%= serverInfo.emailErr %></div>
</div>
<div class="clothes">
<button class="register btn" type="submit">注册</button>
<a class="btn" href="http://localhost:3000/login">
<button type="button">登录</button>
</a>
</div>
</form>
</div>
</body>
</html>
views/user_center.ejs
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8"/>
<title>用户中心</title>
<link rel="stylesheet" type="text/css" href="css/index.css"/>
</head>
<body>
<div id="outer_box" class="login">
<h2><%= serverInfo.uName%> - 个人空间</h2>
</div>
</body>
</html>
db/connectDB.js
const mongoose = require(‘mongoose‘);
module.exports = new Promise((resolve, reject)=>{
mongoose.connect(‘mongodb://localhost:27017/user_database‘, {useNewUrlParser:true})
mongoose.connection.once(‘open‘, err=>{
if(err){
console.log(err);
reject(err);
}else{
resolve(‘数据库已连接‘);
};
});
});
models/userModel.js
const mongoose = require(‘mongoose‘);
const Schema = mongoose.Schema;
const fieldSchema = new Schema({
"userName": {
"type": String,
"unique": true,
"required": true
},
"userPassword": {
"type": String,
"required": true
},
"userEmail": {
"type": String,
"unique": true,
"required": true
},
"createTime": {
"type": Date,
"default": Date.now()
}
});
module.exports = mongoose.model("user_info", fieldSchema);
routers/get/index_router.js
const express = require(‘express‘);
const promiseConnect = require(‘../../db/connectDB.js‘);
const userInfoModel = require(‘../../models/userModel.js‘);
const {resolve} = require(‘path‘);
const cookieParser = require(‘cookie-parser‘);
const indexRouter = new express.Router();
indexRouter.use(cookieParser());
/************************ get ***********************/
indexRouter.get(‘/‘, (request, response)=>{
response.sendFile(resolve(__dirname, ‘../../public/login.html‘));
});
indexRouter.get(‘/login‘, (request, response)=>{
response.sendFile(resolve(__dirname, ‘../../public/login.html‘));
});
indexRouter.get(‘/register‘, (request, response)=>{
response.sendFile(resolve(__dirname, ‘../../public/register.html‘));
});
promiseConnect.then(result=>{
console.log("index_router.js - "+result);
indexRouter.get(‘/user_center‘,async (request, response)=>{
// s4: 解析 cookie 中的 session 去数据库中找对应 sessionId 的数据
const {userId} = request.session; // 返回一个 Cookie 对象
if(userId){ // 用户已经登录
const findRet = await userInfoModel.findOne({"_id":userId});
if(findRet){
let serverInfo = {uName:findRet.userName};
response.render(‘user_center.ejs‘, {serverInfo});
}else{ // 恶意 Cookie
response.clearCookie("userId");
response.redirect(‘/login‘);
};
}else{ // 用户未登录
response.redirect(‘/login‘);
};
});
}).catch(err=>console.log(err));
module.exports = indexRouter;
routers/post/form_router.js
const express = require(‘express‘);
const sha1 = require(‘sha1‘);
const promiseConnect = require(‘../../db/connectDB.js‘);
const userInfoModel = require(‘../../models/userModel.js‘);
const formRouter = new express.Router();
/************************ post ***********************/
let logged = false ;
promiseConnect.then(result=>{
console.log("form_router.js - "+result);
formRouter.post(‘/register‘, async (request, response)=>{
const {
user_name:uName,
user_pwd:uPwd,
user_repeat_pwd:urePwd,
user_email:uEmail,
} = request.body; /**** 解构赋值 ****/
userInfo = {
"userName": uName,
"userPassword": uPwd,
"userEmail": uEmail
};
let serverInfo = {uName, uEmail};
if(urePwd !== uPwd){
serverInfo.repeatErr = ‘密码两次输入不一致‘;
};
if(!(/^[a-zA-Z][a-zA-Z0-9_]{5,20}$/.test(uName))){
serverInfo.nameErr = ‘用户名不合法‘;
};
if(!(/^[a-zA-Z0-9_]{6,20}$/.test(uPwd))){
serverInfo.passwordErr = ‘密码不合法‘;
};
if(!(/^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$/.test(uEmail))){
serverInfo.emailErr = ‘邮箱不合法‘;
};
const fond = await userInfoModel.findOne({"userName": uName});
if(fond){
serverInfo.nameErr = ‘用户名已被注册‘;
};
const badEmail = await userInfoModel.findOne({"userEmail": uEmail});
if(badEmail){
serverInfo.emailErr = ‘邮箱已被注册‘;
};
if(serverInfo.repeatErr || serverInfo.nameErr || serverInfo.passwordErr || serverInfo.emailErr){
response.render(‘register.ejs‘, {serverInfo}); // 渲染错误信息
return;
}else{
userInfo.userPassword = sha1(userInfo.userPassword);
await userInfoModel.create(userInfo);
response.redirect(‘/login‘); // 跳转到登录
};
});
formRouter.post(‘/login‘,async (request, response)=>{
logged = false;
let uName = request.body[‘user_name‘];
let uPwd = request.body[‘user_pwd‘];
userInfo = {
"userName": uName,
"userPassword": uPwd
};
if(!(/^[a-zA-Z][a-zA-Z0-9_]{5,20}$/.test(uName))){
logged = false;
}else if(!(/^[a-zA-Z0-9_]{6,20}$/.test(uPwd))){
logged = false;
};
try{
const findName = await userInfoModel.findOne({"userName": uName});
if(findName && (findName.userPassword===sha1(uPwd)) ){
logged = true;
};
let serverInfo = {uName};
if(logged){
// s3: 设置 session 会在数据库中创建 session 对象
// 保存 userId=findRet.id 到数据库
request.session.userId = findName.id;
response.redirect(‘/user_center‘); // 跳转到用户 个人空间 页面
}else{
serverInfo.tips=‘用户名或密码错误‘;
response.render(‘login.ejs‘, {serverInfo}); // 渲染错误信息
};
}catch(err) {
console.log(err);
}
});
}).catch(err=>console.log(err));
module.exports = formRouter;
index.js
const express = require(‘express‘);
// s1: 导入 session 模块
const session = require(‘express-session‘);
const mongoSession = require(‘connect-mongo‘)(session);
const app = express();
const indexRouter = require(‘./routers/get/index_router.js‘);
const formRouter = require(‘./routers/post/form_router.js‘);
app.set(‘views‘, ‘views‘); // 1. 配置模板路径
app.set(‘view engine‘, ‘ejs‘); // 2. 配置模板引擎
/*********************** 中间件 **********************/
// s2: 通过中间件,激活 session
app.use(session({
secret: ‘myMongoSession‘,
saveUninitialized: false,
resave: false,
store: new mongoSession({
url: ‘mongodb://localhost:27017/user_database‘,
touchAfter: 1000*3600*24
})
}));
// 暴露路由 login.html register.html
app.use(express.static(‘public‘)); // 默认调用 next();
// 将 用户输入的数据 挂载到 请求体 request.body 上
app.use(express.urlencoded({extended: true})); // 默认调用 next();
app.use(indexRouter);
app.use(formRouter);
/**************** 端口号 3000, 启动服务器 ***************/
app.listen(3000, err=>console.log(err?err:‘\n\n服务器已启动: http://localhost:3000\n\t\tHunting Happy!‘));
Node.js_express_临时会话对象 session
原文:https://www.cnblogs.com/baixiaoxiao/p/10573507.html