Cookie和Session
流程: 浏览器post账号密码,服务端校验成功,将session储存(session是存在于服务端的),
给前端设施cookie值(sessionID),以后每次请求携带sessionID.
安装express-session,自动生成sessionID
npm i express-sission
----------------------------------------------
app.js中引入
const session = require("express-session")
// 注册
app.use(session({
name: "system", // cookie名字
secret: "123qwsdqwe", // 密钥 可以随便写
cookie: {
maxAge: 1000 * 60 * 60, // 过期时间,毫秒
secure: false, // false在http中可以获取, true必须在https中才能获取到
},
rolling: true, // 超时前刷新,cookie都会重新计时
resave: true, // 重新设置session后,会重新设置session时间
saveUninitialized: true, // 初始时是否生成cookie, 不影响业务, true时第一次生成的cookie也是无效的
}))
-------------------------------------------------------
登陆接口调用成功时设置session
// 登陆查询
async function selectUser(req, res, next) {
const {username, password } = req.body
const data = await loginService.selectUser(username, password)
if( data.length !== 0 ) {
// 设置session
req.session.user = data[0] // 设置session对象,挂载信息,挂在布尔值也行(就是登陆成功存储session这个过程)默认存储在内存中
res.send({
success: true,
msg: "登陆成功",
data
})
} else {
res.send({success: false, msg: "用户名或密码错误", data})
}
}
----------------------------------------------------------------------------
渲染页面是判断是否具有有效的session
router.get('/', function(req, res, next) {
// 跳转页面时判断一下req.session.user是否存在
if(req.session.user) {
res.render('index', { title: 'Express' });
} else {
res.redirect("/login")
}
});
此时即使cookie过期,只要没有刷新页面,其他接口都可以正常被访问.需要设置中间件解决该问题
app.js中设置中间件
// 设置应用级中间件,进行session过期校验
app.use((req, res, next) => {
// 先排除掉login相关的路由接口,不然会陷入死循环
if(req.url.includes("login")){
next()
return
}
if(req.session.user){
// 重新设置session,达到每次访问接口重新计算session过期时间
req.session.date = Date.now()
next()
} else {
// 判断 是接口,返回错误码, 不是接口 就可以重定向,接口只会返回数据, 重定向是无法生效的
res.redirect("/login")
}
})
退出 销毁session
// 退出登陆
async function logout(req, res, next) {
req.session.destroy(() => {
res.send({
success: true,
msg: "销毁成功"
})
})
}
持久化存储session
connect-mongo中间件
安装模块
npm i connect-mongo
----------------------------------
app.js引入中间件
const MongoStore = require("connect-mongo")
----------------------------------------------------
注册session中间件
app.use(session({
name: "system", // cookie名字
secret: "123qwsdqwe", // 密钥 可以随便写
cookie: {
maxAge: 1000 * 60 * 60, // 过期时间,毫秒
secure: false, // false在http中可以获取, true必须在https中才能获取到
},
resave: true, // 每次访问接口,cookie都会重新计时, false只会按第一次生成的时间计时
saveUninitialized: false, // 初始时是否生成cookie, 不影响业务, true时第一次生成的cookie也是无效的
store: MongoStore.create({
mongoUrl: "mongodb://127.0.0.1:27017/Wzx_test_session", // 新创建了一个数据库, 不是表
ttl: 1000 * 60 * 60 // 过期时间 一定要与上面的保持一致
})
}))
JWT
安装模块
npm i jsonwebtoken
-------------------------------------------------
app.js中引入
const jwt = require("jsonwebtoken")
----------------------------------------------
加密
let token = jwt.sign({
data: "需要加密的数据",
"srcret", // 密钥
{expiresIn: 60 * 60}, // 过期时间(秒)
})
-----------------------------------------------------
校验
let decoded = jwt.verify(token, "srcret") // 密钥要一致
封装
JWT.js文件中
// 引入模块
const jwt = require("jsonwebtoken")
// 设置密钥
const srcret = "Wzx-anyData"
const JWT = {
generate(value, expiresIn){
return jwt.sign(value, srcret, {expiresIn: expiresIn})
},
verify(token){
try {
return jwt.verify(token, srcret)
} catch (error) {
return false
}
},
}
module.exports = JWT
登陆时设置token, 放在header的Authorization中
// 登陆查询
async function selectUser(req, res, next) {
const {username, password } = req.body
const data = await loginService.selectUser(username, password)
if( data.length !== 0 ) {
// 设置token
const token = JWT.generate({
_id: data[0]._id,
username: data[0].username
}, "1h")
// token返回在header中
res.header("Authorization", token)
res.send({
success: true,
msg: "登陆成功",
data,
})
} else {
res.send({success: false, msg: "用户名或密码错误", data})
}
}
--------------------------------------------------------------------
前端在axios的响应拦截器中存储token
axios.interceptors.response.use((res) => {
const { authorization } = res.headers
authorization && localStorage.setItem("token", authorization )
return res
}, (err) => {
// token失效 后端设置的401
if(err.response.status === 401) {
// 前端删除token
localStorage.removeItem("token")
}
return Promise.reject(err)
})
------------------------------------------------------------
前端每次请求时携带token
axios.interceptors.request.use((config) => {
const token = localStorage.getItem("token")
// 习惯拼接Bearer加空格token
config.headers.authorization = `Bearer ${token}`
return config
})
--------------------------------------------------------------------
后端校验token
app.use((req, res, next) => {
// 先排除掉login相关的路由接口,不然会陷入死循环
if(req.url.includes("login")){
next()
return
}
// 可选链 req.headers["authorization"]为true时, 才会执行split
// 如果是第一次进入,没有token, 此时的req.headers["authorization"]是一个null字符串
const token = req.headers["authorization"]?.split(" ")[1]
if(token) {
const payload = JWT.verify(token)
if(payload){
// 重新计算token过期时间
const newToken = JWT.generate({
_id: payload._id,
username: payload.username
}, "1h")
res.header("Authorization", newToken)
next()
} else {
res.status(401).send({
success: false,
msg: "token过期"
})
}
} else {
next()
}
})