1. Cookie&Session
「HTTP
无状态」
我们知道,
HTTP
是无状态的。也就是说,
HTTP 请求方和响应方间无法维护状态, 都是一次性的,它不知道前后的请求都发生了什么。但有的场景下,我们需要维护状态。最典型的,一 个用户登陆CSDN,发布、关注、评论,都应是在登录后的用户状态下的。「标记」那解决办法是什么 呢?
![](https://i-blog.csdnimg.cn/blog_migrate/ee42efc89830a710ed65d8eb33fb7b0c.png)
1.下载插件
npm i express-session // 操作session
npm i connect-mongo // 将动态的session存到MongoDB的数据库中
2.在app.js中写代码
//引入session
var session = require('express-session')
const MongoStore = require("connect-mongo");
// 注册session中间件
app.use(session({
name: "xiaogong", // 名字
secret: "xiaogongxinhaiganyukeqing", // 秘钥
cookie: {
maxAge: 1000 * 60 * 60, // maxAge表示cookie有效时间
secure:false // secure为 true 时候表示只有 https 协议才能访问cookie
},
resave: true, // 重新设置session后,会自动重新计算过期时间
saveUninitialized: true, // 先给一个无效cookie
store: MongoStore.create({
mongoUrl: 'mongodb://127.0.0.1:27017/user_session', // 新创建一个数据库存用户session
ttl: 1000 * 60 * 10 // session有效时间
})
}))
// 设置中间件,session过期校验
app.use((req, res, next) => {
//排除login相关的路由和接口
if (req.url.includes("login")) {
next()
return
}
if (req.session.user) {
// 重新设置session
req.session.date=Date.now()
next()
} else {
// 是接口返回错误码,不是接口就重定向
req.url.includes("api")?res.status(401).json({ok:0}):res.redirect('/login')
}
})
注意:注册和设置中间件要在你创建的路由前面,否则就白写了
3.在routes文件夹里创建user.js文件,写代码
var express = require('express')
const UserController = require('../controller/UserController')
var router = express.Router()
//登录
router.post('/login', UserController.login)
// 退出登录
router.get('/logout',UserController.logout)
module.exports = router
4.在controller文件夹中创建文件UserController.js文件,写代码
const UserService=require('../service/UserService')
const UserController= {
login: async (req, res)=> {
const { username, password } = req.body
const data = await UserService.login(username, password)
if (data.length === 0) {
res.send({
ok:0
})
} else {
// 设置session对象
req.session.user=data[0]
res.send({
ok: 1
})
}
},
logout: (req, res) => {
req.session.destroy(() => {
res.send({
ok:1
})
})
}
}
module.exports=UserController
5.创建model文件夹,创建文件UserModel.js,写代码
const mongoose = require('mongoose')
// 设置字段类型
const Schema=mongoose.Schema
const UserType = {
username: String,
password: String,
age:Number
}
// 模型 user 将会对应 users 集合
const UserModel = mongoose.model("user",new Schema(UserType))
module.exports=UserModel
6.创建service文件夹,创建文件UserService.js,写代码
// 操作数据库
const UserModel=require('../model/UserModel')
const UserService = {
login: (username,password) => {
return UserModel.find({username,password})
}
}
module.exports=UserService
前端login.ejs
<!DOCTYPE html>
<html lang="en">
<head>
<title></title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
<div>
<div>用户名:<input type="text" id="username"></div>
<div>密码:<input type="password" id="password"></div>
<button id="login">登录</button>
</div>
<script>
var username = document.querySelector('#username')
var password = document.querySelector('#password')
var loginFun = document.querySelector('#login')
//登录
loginFun.onclick = () => {
fetch('/api/login', {
method: "POST",
body: JSON.stringify({
username: username.value,
password: password.value,
}),
headers: {
"Content-Type": "application/json"
}
}).then(res => res.json()).then(res => {
if (res.ok === 1) {
location.href = '/'
} else {
alert('密码错误!')
}
})
}
</script>
</body>
</html>
index.ejs
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div>
<h1>mongodb增删改查</h1>
<div>
<div>用户名:<input type="text" id="username"></div>
<div>密码:<input type="password" id="password"></div>
<div>年龄:<input type="number" id="age"></div>
<button id="logout">退出登录</button>
</div>
<button id="register">注册</button>
<hr>
<button id="update">更新</button>
<hr>
<button id="delete">删除</button>
<hr>
<button id="search">查询</button>
</div>
<script>
var username = document.querySelector('#username')
var password = document.querySelector('#password')
var age = document.querySelector('#age')
var registerFun = document.querySelector('#register')
var updateFun = document.querySelector('#update')
var deleteFun = document.querySelector('#delete')
var searchFun = document.querySelector('#search')
var logoutFun = document.querySelector('#logout')
// 增
registerFun.onclick = () => {
fetch("/api/user/", {
method: "POST",
body: JSON.stringify({
username: username.value,
password: password.value,
age: age.value,
}),
headers: {
"Content-Type": "application/json"
}
}).then(res => res.json()).then(res => {
console.log(res);
})
}
// 删
deleteFun.onclick = () => {
fetch("/api/user/62e748d9332656d2c168621d", {
method: "DELETE",
}).then(res => res.json()).then(res => {
console.log(res);
})
}
// 改
updateFun.onclick = () => {
fetch("/api/user/62e748d9332656d2c168621d", {
method: "PUT",
body: JSON.stringify({
username: "宵宫",
password: "密码",
age: 1,
}),
headers: {
"Content-Type": "application/json"
}
}).then(res => res.json()).then(res => {
console.log(res);
})
}
// 查
searchFun.onclick = () => {
fetch("/api/user/?page=1&limit=2", {
method: 'GET'
}).then(res => res.json()).then(res => {
console.log(res);
})
}
// 退出登录
logoutFun.onclick = () => {
fetch("api/logout", {
method: "GET"
}).then(res => res.json()).then(res => {
if (res.ok === 1) {
location.href = "/login"
}
})
}
</script>
</body>
</html>
测试:
没登录时,会有一个没有用的cookie,此时直接在地址栏进http://localhost:3000是进不去的,会被直接重定向到http://localhost:3000/login
登录之后,会发现数据库会自动新建一个新的数据库user_session,存的数据expires就是session的有效期,刷新页面,session的有效期也会改变,这样用户在登录状态时只要访问接口,就不会使session失效。
Cookie&Session的缺点:用户数据过多有可能会使数据库崩溃,不同数据库之间的session难以共享。