前后端分离(蜗牛学苑05)-上传图片前后端处理,图片预览,数据库保存图片数据,数据加密,md5、bcypt加密,认证流程,生成token,前端请求携带token,token配置,认证后处理,统一处理

本文主要是介绍前后端分离(蜗牛学苑05)-上传图片前后端处理,图片预览,数据库保存图片数据,数据加密,md5、bcypt加密,认证流程,生成token,前端请求携带token,token配置,认证后处理,统一处理,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

1、图片上传前端处理

1、index.html

 <div><label>上传头像</label><input type="file" id="upload"></div><div class="imag-box"><!-- 显示图片预览 --><img src="" alt=""/></div><div><button id="addStudent">确认新增</button></div>
  //图片上传$('#upload').change(function () {//1、选中图片的相关信息//console.log(this)const files=this.files//2、处理图片信息,添加到表单对象formData中,这是原生js提供的const formData=new FormData()//formData的append方法添加formData对象,有两个参数,第一个是自定义的文件名,第二个是图片信息formData.append('file',files[0])//3、通过ajax将文件发送出去,实际应该是formData发送出去$.ajax({url:'/images/upload',type:'POST',data:formData,//cache:是否读取缓存中的结果,cache:false,//数据编码格式是否适用jquery方法contentType:false,//发送的图片数据是否转换为其他格式processData:false,success(msg){console.log(msg)}})})
2、图片上传后端处理

需安装multer文件上传插件

npm i multer

1、为上传按钮的change事件添加逻辑

//图片上传$('#upload').change(function () {//1、选中图片的相关信息//console.log(this)const files=this.files//2、处理图片信息,添加到表单对象formData中,这是原生js提供的const formData=new FormData()//formData的append方法添加formData对象,有两个参数,第一个是自定义的文件名,第二个是图片信息formData.append('file',files[0])//3、通过ajax将文件发送出去,实际应该是formData发送出去$.ajax({url:'/images/upload',type:'POST',data:formData,//cache:是否读取缓存中的结果,cache:false,//数据编码格式是否适用jquery方法contentType:false,//发送的图片数据是否转换为其他格式processData:false,success(msg){console.log(msg)}})})

2、在app.js里添加一级路由

const imagesRouter=require('./routes/images')app.use('/images', imagesRouter)

3、在routes里添加images路由

var express = require('express');
var router = express.Router();//引入文件上传插件
const { uploadFiles }=require('../utils/handleFiles')router.post('/upload', async function(req, res, next) {//这里返回的upload依然还是一个方法const upload=uploadFiles({//设置上传成功后的图片存储路径path:'./public/images',//和前端传来的fromData的第一个参数需要一致,也就是‘file’(formData.append('file',files[0]))key:'file',//上传图像大小限制单位是kbsize:1000})upload(req,res,function (err) {if (err){console.log('图片上传失败')}else{console.log('图片上传成功',req.files)}})});module.exports = router;

4、建立一个utlis目录,创建第三方编写好的handleFile.js,里面exports出四个文件操作方法
handleFile.js

// 文件上传 npm i multer
const multer = require('multer');
const fs = require('fs');
const path = require('path');/*** 文件上传* 参数说明:接收一个 options 对象作为参数,该对象包含三个属性* - path:图片上传路径* - key:与前端 formData 对象的 fieldname 相匹配(即 formData.append()方法的第一个参数)* - size: 设置图片最大限制,单位 kb*/
function uploadFiles(options = {}) {// 1. 对参数 options 进行解构并设置默认值const { path = "./public/temp", key = "file", size = 1000 } = options;// 2. 设置 multer 的参数,配置 diskStorage,来控制文件存储的位置以及文件名字等const storage = multer.diskStorage({// 2.1 确定图片存储的位置destination: function (req, file, cb) {// 当 path 所对应目录不存在时,则自动创建该文件try {fs.accessSync(path);} catch (error) {fs.mkdirSync(path);}cb(null, path);},// 2.2 确定图片存储时的名字。(注意:如果使用原名,可能会造成再次上传同一张图片的时候的冲突)filename: function (req, file, cb) {var changedName = new Date().getTime() + '-' + file.originalname;cb(null, changedName);}});// 3. 配置图片限制const limits = {// 限制文件大小 1000 kbfileSize: 1024 * size,// 限制文件数量files: 10};// 4.生成的专门处理上传的一个工具,可以传入 storage、limits 等配置const upload = multer({ storage, limits });// 5. 返回多文件上传的设置信息(同样可用于单文件上传)return upload.array(key);
}
/*** 文件移动* 参数说明:接收一个 options 对象作为参数,该对象包含三个属性* - fromPath:文件原路径* - toPath:文件新路径* - filename:文件名*/
function moveFiles({ fromPath, toPath, filename } = {}) {if (!filename) {console.log('文件移动失败:filename 文件名不能为空');return;}// 要移动的文件的原路径const sourceFile = path.join(fromPath, filename);// 判断源文件是否存在try {fs.accessSync(sourceFile);} catch (error) {console.log('文件移动失败:' + sourceFile + ' 该文件不存在。');return;}// 判断文件要移动的新路径是否存在,如果不存在,则创建try {fs.accessSync(toPath);} catch (error) {fs.mkdirSync(toPath);}// 文件移动后的新路径const newFile = path.join(toPath, filename);fs.renameSync(sourceFile, newFile);
}/*** 删除文件* 参数说明:该函数接收一个路径字符串作为参数,传递的路径即为要删除的文件路径**/function deleteFiles(dir) {// 判断 dir 是否存在try {fs.accessSync(dir);} catch (error) {console.log(dir + ' 该路径不存在。');return;}try {// 判断 dir 是文件还是文件夹const stats = fs.statSync(dir);if (stats.isFile()) {fs.unlinkSync(dir);}if (stats.isDirectory()) {// 判断该文件是否为空const files = fs.readdirSync(dir);files.forEach(item => {deleteFiles(path.join(dir, item));});// 删除空文件夹fs.rmdirSync(dir);}} catch (error) {console.log('文件删除失败:');console.log(error);}
}
function copyFiles(oldPath, newPath) {// 判断 oldPath 是否存在try {fs.accessSync(oldPath);// 判断路径是文件还是文件夹const stats = fs.statSync(oldPath);if (stats.isFile()) {// 读取 源文件的内容const data = fs.readFileSync(oldPath, 'utf-8');// 将读取的内容追加到新文件中fs.appendFileSync(newPath, '\n' + data);}if (stats.isDirectory()) {console.log(oldPath, '该路径不是文件名');}} catch (error) {console.log(oldPath, '该路径不存在');}}
module.exports = {uploadFiles, moveFiles, deleteFiles,copyFiles
}

5、目录结构为
在这里插入图片描述

3、图片上传成功后的预览

1、images.js中的路由中设置上传成功后的业务逻辑

router.post('/upload', async function(req, res, next) {//这里返回的upload依然还是一个方法const upload=uploadFiles({//设置上传成功后的图片存储路径path:'./public/images',//和前端传来的fromData的第一个参数需要一致,也就是‘file’(formData.append('file',files[0]))key:'file',//上传图像大小限制单位是kbsize:1000})upload(req,res,function (err) {if (err){console.log('图片上传失败')}else{console.log('图片上传成功',req.files)res.send({message:'图片上传成功',status:1,data:req.files[0].filename})}})});

2、index.html中ajax处理回传数据并预览在页面中

//图片上传$('#upload').change(function () {//1、选中图片的相关信息//console.log(this)const files=this.files//2、处理图片信息,添加到表单对象formData中,这是原生js提供的const formData=new FormData()//formData的append方法添加formData对象,有两个参数,第一个是自定义的文件名,第二个是图片信息formData.append('file',files[0])//3、通过ajax将文件发送出去,实际应该是formData发送出去$.ajax({url:'/images/upload',type:'POST',data:formData,//cache:是否读取缓存中的结果,cache:false,//数据编码格式是否适用jquery方法contentType:false,//发送的图片数据是否转换为其他格式processData:false,success(msg){if (msg.status){$('#students-images').attr('src',`./images/${msg.data}`)}}})})
4、数据库保存学生图片数据

1、index.html新增学生里面添加imagename的内容

//新增学生$('#addStudent').click(function(){const name=$('#addStudentsName').val()const age=$('#addStudentsAge').val()const gender=$('[name=addStudentsGender]:checked').val()const classId=$('#selectClasses').val()const imageName=$('#students-images').data('src')//console.log(imageName)//console.log(name,age,gender)$.ajax({url:'/students/addStudents',type:'POST',data:{name,age,gender,classId,imageName},success(msg){if (msg.status){getStudents()}}})})

2、studentsModel里添加对于imageName的描述

//数据集合的相关配置
//1、定义数据集合的结构:定义集合中数据有哪些属性,属性的值是什么类型(数据库结构)
//解构出mongoose的Schema方法来,这个方法用来操作集合的
const { Schema,model }=require('mongoose')
//通过构造函数创建集合的结构
const studentsSchema=new Schema({name:String,age:String,gender:String,imageName:String,//用于关联classes集合classId:{type:Schema.Types.ObjectId,//ref用于设置要关联的集合的模型名称ref:'classesModel'}
},{versionKey:false})//2、定义数据集合的模型:将schema和数据库中的集合关联起来
//model需要三个参数:模型名称,schema名称,数据库中的集合名称
const studentsModel=model('studentsModel',studentsSchema,'students')//向第三层dao层暴露model数据
module.exports.studentsModel=studentsModel

3、mongodb中发现students集合中文档多了一个imageName属性
在这里插入图片描述

5、学生列表渲染图片

||或运算符,如果符号前为真,则使用符号前面的值,如果为假使用后面的值

<img width="80" src="./images/${item.imageName || 'default.jpg'}" alt="">

index.html中getstudents()函数进行改造

//获取全部学生function getStudents(){$.ajax({url:'/students/getStudents',data: {currentPage: data.currentPage,pageSize:data.pageSize,searchValue: data.searchValue,searchType: data.searchType},type:'GET',success(msg){//进入if表示进入学生数据成功if(msg.status){data={...data,...msg.data}console.log(msg.data)//渲染数据const str=msg.data.students.map(function (item,index) {return `<tr><td>${item.name}</td><td>${item.age}</td><td>${item.gender}</td><td>${item.classId.name}</td><td>${item.classId.teachersId.map(item=>`<p>${item.name}</p>`).join('')}</td><td><img width="80" src="./images/${item.imageName || 'default.jpg'}" alt=""></td><td><button class="updateBtn" data-id="${item._id}">修改</button><button class="removeBtn" data-id="${item._id}">删除</button></td></tr>`}).join('')//join是拼接$('tbody').html(str)//渲染下分页指示$('#currentPage').text(data.currentPage)$('#pages').text(data.pages)$('#total').text(data.total)}}})}
6、多余图片的处理

删除public/images下面没有被数据库所使用的图片,其实应该在点击上传文件的时候,图片不能直接上传,只能作为零临时文件。

1、index.html修改上传文件路径为temp

  //图片上传$('#upload').change(function () {//1、选中图片的相关信息//console.log(this)const files=this.files//2、处理图片信息,添加到表单对象formData中,这是原生js提供的const formData=new FormData()//formData的append方法添加formData对象,有两个参数,第一个是自定义的文件名,第二个是图片信息formData.append('file',files[0])//3、通过ajax将文件发送出去,实际应该是formData发送出去$.ajax({url:'/images/upload',type:'POST',data:formData,//cache:是否读取缓存中的结果,cache:false,//数据编码格式是否适用jquery方法contentType:false,//发送的图片数据是否转换为其他格式processData:false,success(msg){if (msg.status){$('#students-images').attr('src',`./temp/${msg.data}`).data('src',msg.data)//创建自定义属性,这里相当于创建了一个data-src属性,并将msg.data图片路径保存在data-src属性中}}})})

2、students.js路由中使用moveFiles和deleteFiles函数对temp和images两个目录中的文件进行操作

var express = require('express');
var router = express.Router();//引入第二层的getStudents方法
const {getStudents,addStudents,deleteStudents,getStudentsById,updateStudents}=require('../service/studentsServices')//引入文件处理模块
const {moveFiles,deleteFiles}=require('../utils/handleFiles')//获取所有数据,及查询
router.get('/getStudents', async function(req, res, next) {//console.log('第一层的模糊查询:',req.query)const data=await getStudents(req.query)res.send(data)});//删除学生
router.post('/deleteStudents', async function(req, res, next) {const data=await  deleteStudents(req.body)res.send(data)});//新增学生
router.post('/addStudents', async function(req, res, next) {const data=await addStudents(req.body)if (data.status && req.body.imageName){moveFiles({fromPath:'./public/temp',toPath:'./public/images',filename:req.body.imageName})deleteFiles('./public/temp')//直接把文件夹给删除了}res.send(data)
});//获取一个学生
router.get('/getStudentsById', async function(req, res, next) {const data=await getStudentsById(req.query)res.send(data)
});//确认修改一个学生
router.post('/updateStudents', async function(req, res, next) {const data=await updateStudents(req.body)res.send(data)
});
module.exports = router;
7、删除分页的优化处理

对删除按钮委托函数进行逻辑优化
核心是删除最后一个数据,且该页只有一个数据,删除后页码需要向前跳动一个

index.html

//删除按钮使用事件委托//on做委托,委托给父级的静态元素,第一个参数是事件,第二个参数是真正执行的那个标签(委托人),第三个参数是具体的工作$('tbody').on('click','.removeBtn',function(){//获取当前点击的这个按钮对应的id,data('id')是获取所有(特定)属性的函数,这里的id实际就是data-id,前面加data-是一种约定俗成const _id=$(this).data('id')console.log(_id)$.ajax({url:'/students/deleteStudents',type:'post',data: { _id },success (msg) {if (msg.status){alert('删除成功')//data.currentPage=1//如果删除最后一条if (data.students.length==1){//当前页要往前退一个data.currentPage--}//重新发一次ajax请求getStudents()}}})})
8、数据加密

1、可逆
2、不可逆
2.1有规律md5
2.2无规律bcrypt
utils/crypto.js

// crypto.jsconst crypto = require("crypto"); // Node.js 中内置该模块,不用下载/*** @md5加密模块 (加密固定,不可逆)* @param str string 要加密的字符串* @param secret string 要使用的加密密钥* @retrun string 加密后的字符串* */
module.exports.getMd5 = function (str, secret = '9vApxLk5G3PAsJrM') {const md5 = crypto.createHash('md5');return md5.update(str + secret).digest('hex');
}
/*** @aes128加密模块* @param str string 要加密的字符串* @param secret string 要使用的加密密钥(要记住,不然就解不了密啦)* @retrun string 加密后的字符串*/
module.exports.getEncAse128 = function (str, secret = '9vApxLk5G3PAsJrM', iv = 'FnJL7EDzjqWjcaY9') {const cipheriv = crypto.createCipheriv('aes-128-cbc', secret, iv);var enc = cipheriv.update(str, "utf8", "hex");    //编码方式从utf-8转为hex;enc += cipheriv.final("hex"); //编码方式从转为hex;return enc; //返回加密后的字符串
}
/*** @aes128解密模块* @param str string 要解密的字符串* @param secret string 要使用的解密密钥(要和密码的加密密钥对应,不然就解不了密啦)* @retrun string 解密后的字符串* */
module.exports.getDecAse128 = function (str, secret = '9vApxLk5G3PAsJrM', iv = 'FnJL7EDzjqWjcaY9') {var decipheriv = crypto.createDecipheriv("aes-128-cbc", secret, iv);var dec = decipheriv.update(str, "hex", "utf8");//编码方式从hex转为utf-8;dec += decipheriv.final("utf8");//编码方式从utf-8;return dec;
}/*** @Hmac-sha1加密模块 (每次加密随机,不可逆)* @param str string 要加密的字符串* @param secret string 要使用的加密密钥* @retrun string 加密后的字符串* */
module.exports.getHmac = function (str, secret = '9vApxLk5G3PAsJrM') {var buf = crypto.randomBytes(16);secret = buf.toString("hex");//密钥加密;var Signture = crypto.createHmac("sha1", secret);//定义加密方式Signture.update(str);var miwen = Signture.digest().toString("base64");//生成的密文后将再次作为明文再通过pbkdf2算法迭代加密;return miwen;
}
/*** @sha1加密模块 (加密固定,不可逆)* @param str string 要加密的字符串* @retrun string 加密后的字符串* */
module.exports.getSha1 = function (str) {var sha1 = crypto.createHash("sha1");//定义加密方式:md5不可逆,此处的md5可以换成任意hash加密的方法名称;sha1.update(str);var res = sha1.digest("hex");  //加密后的值dreturn res;
}
9、md5加密登录注册

修改uers.js的传值,注册传值过程中直接加密,登录是也同样加密,注册时,密码直接以加密后的形式存储在数据库中
uers.js

const express = require('express')
const router = express.Router()
// //模拟数据库
// const users = [
//   { username: 'zhangsan', password: 123 },
//   { username: 'lisi', password: 456 },
// ]const {getMd5}=require('../utils/crypto')//引入服务层的方法
const { login,isAcces,register } = require('../service/usersServices')
//登录路由数据库版本
router.post('/login', async function (req, res, next) {//接受到前端发送的用户数据const { username,password } = req.bodyconst newPassword=getMd5(password)const data = await login({username,password:newPassword})res.send(data)})/* GET users listing. */
router.get('/', function (req, res, next) {res.send('respond with a resource')
})
//设置二级路由//注册路由
router.post('/register', async function (req, res, next) {const { username,password } = req.body//注册阶段在这里加密const newPassword=getMd5(password)//console.log('加密后的密码',newPassword)const data=await register({username,password:newPassword})res.send(data)})
//验证用户名是否存在路由
router.post('/isAccess', async function (req, res, next) {const { username } = req.bodyconst data=await isAcces(username)res.send(data)})module.exports = router
10、bcypt加密登录注册

bcypt模块下载安装,貌似内置模块不用下载

npm install bcrypt

注册模块用bcypt的hashSync()加密,登录模块从数据库读用户的密码字段,然后用compareSync()去和当前登录密码比较

const express = require('express')
const router = express.Router()const {getMd5}=require('../utils/crypto')
const bcrypt = require("bcryptjs");//引入服务层的方法
const { login,isAcces,register } = require('../service/usersServices')
//登录路由数据库版本
router.post('/login', async function (req, res, next) {//接受到前端发送的用户数据const { username,password } = req.body//用md5加密//const newPassword=getMd5(password)// const data = await login({username,password:newPassword})// res.send(data)//现密码和数据库里的密码进行比较//const { data } = await login({username})//data[0].password是数据库中加密的密码const isok=  bcrypt.compareSync(password,data[0].password)if (isok){res.send({message:'登录成功',status:1,})}else{res.send({message:'登录失败',status:0,})}})/* GET users listing. */
router.get('/', function (req, res, next) {res.send('respond with a resource')
})
//设置二级路由//注册路由
router.post('/register', async function (req, res, next) {const { username,password } = req.body//注册阶段在这里加密,使用md5加密//const newPassword=getMd5(password)//console.log('加密后的密码',newPassword)//第二个参数是加密强度,用10就很好const newPassword=bcrypt.hashSync(password,10)console.log('加密密码:',newPassword)const data=await register({username,password:newPassword})res.send(data)})
//验证用户名是否存在路由
router.post('/isAccess', async function (req, res, next) {const { username } = req.bodyconst data=await isAcces(username)res.send(data)})module.exports = router
11、身份认证流程

身份验证示意图
在这里插入图片描述

身份验证流程

  1. 前端发送登录请求到后端,后端通过账号密码来判断用户是否登录成功;
  2. 如果登录成功,后端生成 token,然后将 token 连同“登录成功”的信息一并返回给前端;
  3. 前端接收到 token 后,将 token 保存在 localStorage 中;
  4. 后续前端发送的每一个请求,都需要将 token 携带上,一并发送到后端;
  5. 服务端接收到每一个请求,都需要先对请求中的 token 进行验证,验证通过后,才让该请求发送到对应的表现层中去;
  6. 如果后端 token 验证没有通过,后端直接返回 401 的报错给前端(请求无法发送到表现层);
  7. 如果前端用户要退出登录,直接将 localStorage 中的 token 删除即可;
12、node生成token

下载依赖包

  • jsonwebtoken:该依赖包用于在服务端生成 token、解码 token;
  • express-jwt:该依赖包用于在服务端验证 token 是否有效;
npm i jsonwebtoken express-jwt --save

1、在user.js中生成token

const express = require('express')
const router = express.Router()
// //模拟数据库
// const users = [
//   { username: 'zhangsan', password: 123 },
//   { username: 'lisi', password: 456 },
// ]const {getMd5}=require('../utils/crypto')
const bcrypt = require("bcryptjs");
const jwt=require('jsonwebtoken')//引入服务层的方法
const { login,isAcces,register } = require('../service/usersServices')
//登录路由数据库版本
router.post('/login', async function (req, res, next) {//接受到前端发送的用户数据const { username,password } = req.body//用md5加密//const newPassword=getMd5(password)// const data = await login({username,password:newPassword})// res.send(data)//现密码和数据库里的密码进行比较//const { data } = await login({username})//data[0].password是数据库中加密的密码const isok=  bcrypt.compareSync(password,data[0].password)if (isok){//生成tokenconst token=jwt.sign({username},//要保存的用户信息'fengray',//秘钥,用来加密混入字符串{expiresIn: 60}//设置token的有效期,单位为秒)res.send({message:'登录成功',status:1,token})}else{res.send({message:'登录失败',status:0,})}})/* GET users listing. */
router.get('/', function (req, res, next) {res.send('respond with a resource')
})
//设置二级路由//注册路由
router.post('/register', async function (req, res, next) {const { username,password } = req.body//注册阶段在这里加密,使用md5加密//const newPassword=getMd5(password)//console.log('加密后的密码',newPassword)//第二个参数是加密强度,用10就很好const newPassword=bcrypt.hashSync(password,10)console.log('加密密码:',newPassword)const data=await register({username,password:newPassword})res.send(data)})
//验证用户名是否存在路由
router.post('/isAccess', async function (req, res, next) {const { username } = req.bodyconst data=await isAcces(username)res.send(data)})module.exports = router

2、在login.html中保存token在localStorage中

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Title</title>
</head>
<body><h2>用户登录</h2><label>用户名</label><input type="text" name="username" id="username"><br/><label>密码</label><input type="text" name="password" id="password"><br/><button id="loginBtn">登录</button><script src="./javascripts/jquery.min.js"></script><script>$('#loginBtn').click(function(){const username=$('#username').val()const password=$('#password').val()$.ajax({//接口url:'/users/login',//前端发送给后端的数据data:{username,password},//请求类型type:'POST',//请求成功后触发success方法,msg用于接收后端响应来的结果success(msg){//console.log(msg.token)if (msg.status){//msg.status取值范围为1和0,对应真和假,非零为真alert('登录成功')//在localStorage中保存localStorage.setItem('token',msg.token)//localStorage.token=msg.token//等效上一句代码}else{alert('登录失败')}}})})</script></body>
</html>
13、前端发送请求携带token

请求消息由四部分组成
1、请求头
2、请求行
3、空行
4、请求体
在进入首页时就执行一个isLogin函数,在这个函数里面发送ajax请求,同时设置请求头信息,并在请求头信息里面带上localStorage里存储的token
index.html

 //进入到页面的时候就要进行验证用户是否登录isLogin()function isLogin(){$.ajax({url:'/users/isLogin',type:'GET',//请求头信息,放入token信息headers:{//Bearer是token的前缀,必须要有,注意需要有个空格Authorization:'Bearer '+localStorage.token},success(msg){console.log(msg)}})}
14、token验证配置

专门配置一个文件来执行验证:utils/jwt.js

1、jwt.js

//定义验证规则
const expressJWT = require('express-jwt')
//执行验证函数const jwtAuth = expressJWT({secret: 'fengray',//使用jwt生成token时填入的秘钥字符串algorithms: ['HS256'],//设置jwt的算法为HS256credentialsRequired: false,//对于没有token的请求不进行解析
}).unless({//用于设置不需要验证token的路径path: ['/users/login','/users/register','/users/isAccess']
})module.exports = jwtAuth

2、在app.js中引入这个jwt验证配置
app.js

const jwtAuth=require('./utils/jwt')
//在一级路径之前来进行验证
app.use(jwtAuth)
15、身份认证成功或失败后的处理

index.html中设置验证失败的逻辑
1、index.html

  //进入到页面的时候就要进行验证用户是否登录isLogin()function isLogin(){$.ajax({url:'/users/isLogin',type:'GET',//请求头信息,放入token信息headers:{//Bearer是token的前缀,必须要有,注意需要有个空格Authorization:'Bearer '+localStorage.token},success(msg){console.log(msg)},error(err){if (err.status==401){alert('您尚未登录,请先登录')location.href='./login.html'}}})}

在user.js中设置访问路由的逻辑,
2user.js

//验证是否登录
router.get('/isLogin', async function (req, res, next) {//获取tokenconst headersToken=req.get('Authorization')const token = headersToken.split(' ')[1]//console.log(token)// 并解码token并拿到用户信息,需要传入生成token是设定的秘钥字符串const { username }=jwt.verify(token,'fengray')//console.log(username)res.send({message:'身份验证通过',status:1,data:username})})
16、ajax的统一处理

几乎所有的请求都应当带token因此,直接对ajax进行统一设置
index.html
这段代码写在script的最开头

//批量给ajax设置请求头// $.ajaxSettings.beforeSend=function(xhr,requst){//   //给xhr设置请求头//   xhr.setRequestHeader('Authorization','Bearer '+localStorage.token)// }//批量给ajax设置错误处理$.ajaxSetup({headers:{//Bearer是token的前缀,必须要有,注意需要有个空格Authorization:'Bearer '+localStorage.token},error(err){if (err.status==401){alert('您尚未登录,请先登录')location.href='./login.html'}}})

这篇关于前后端分离(蜗牛学苑05)-上传图片前后端处理,图片预览,数据库保存图片数据,数据加密,md5、bcypt加密,认证流程,生成token,前端请求携带token,token配置,认证后处理,统一处理的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/382695

相关文章

Vue3 的 shallowRef 和 shallowReactive:优化性能

大家对 Vue3 的 ref 和 reactive 都很熟悉,那么对 shallowRef 和 shallowReactive 是否了解呢? 在编程和数据结构中,“shallow”(浅层)通常指对数据结构的最外层进行操作,而不递归地处理其内部或嵌套的数据。这种处理方式关注的是数据结构的第一层属性或元素,而忽略更深层次的嵌套内容。 1. 浅层与深层的对比 1.1 浅层(Shallow) 定义

Security OAuth2 单点登录流程

单点登录(英语:Single sign-on,缩写为 SSO),又译为单一签入,一种对于许多相互关连,但是又是各自独立的软件系统,提供访问控制的属性。当拥有这项属性时,当用户登录时,就可以获取所有系统的访问权限,不用对每个单一系统都逐一登录。这项功能通常是以轻型目录访问协议(LDAP)来实现,在服务器上会将用户信息存储到LDAP数据库中。相同的,单一注销(single sign-off)就是指

浅析Spring Security认证过程

类图 为了方便理解Spring Security认证流程,特意画了如下的类图,包含相关的核心认证类 概述 核心验证器 AuthenticationManager 该对象提供了认证方法的入口,接收一个Authentiaton对象作为参数; public interface AuthenticationManager {Authentication authenticate(Authenti

Spring Security基于数据库验证流程详解

Spring Security 校验流程图 相关解释说明(认真看哦) AbstractAuthenticationProcessingFilter 抽象类 /*** 调用 #requiresAuthentication(HttpServletRequest, HttpServletResponse) 决定是否需要进行验证操作。* 如果需要验证,则会调用 #attemptAuthentica

大模型研发全揭秘:客服工单数据标注的完整攻略

在人工智能(AI)领域,数据标注是模型训练过程中至关重要的一步。无论你是新手还是有经验的从业者,掌握数据标注的技术细节和常见问题的解决方案都能为你的AI项目增添不少价值。在电信运营商的客服系统中,工单数据是客户问题和解决方案的重要记录。通过对这些工单数据进行有效标注,不仅能够帮助提升客服自动化系统的智能化水平,还能优化客户服务流程,提高客户满意度。本文将详细介绍如何在电信运营商客服工单的背景下进行

基于MySQL Binlog的Elasticsearch数据同步实践

一、为什么要做 随着马蜂窝的逐渐发展,我们的业务数据越来越多,单纯使用 MySQL 已经不能满足我们的数据查询需求,例如对于商品、订单等数据的多维度检索。 使用 Elasticsearch 存储业务数据可以很好的解决我们业务中的搜索需求。而数据进行异构存储后,随之而来的就是数据同步的问题。 二、现有方法及问题 对于数据同步,我们目前的解决方案是建立数据中间表。把需要检索的业务数据,统一放到一张M

这15个Vue指令,让你的项目开发爽到爆

1. V-Hotkey 仓库地址: github.com/Dafrok/v-ho… Demo: 戳这里 https://dafrok.github.io/v-hotkey 安装: npm install --save v-hotkey 这个指令可以给组件绑定一个或多个快捷键。你想要通过按下 Escape 键后隐藏某个组件,按住 Control 和回车键再显示它吗?小菜一碟: <template

关于数据埋点,你需要了解这些基本知识

产品汪每天都在和数据打交道,你知道数据来自哪里吗? 移动app端内的用户行为数据大多来自埋点,了解一些埋点知识,能和数据分析师、技术侃大山,参与到前期的数据采集,更重要是让最终的埋点数据能为我所用,否则可怜巴巴等上几个月是常有的事。   埋点类型 根据埋点方式,可以区分为: 手动埋点半自动埋点全自动埋点 秉承“任何事物都有两面性”的道理:自动程度高的,能解决通用统计,便于统一化管理,但个性化定

无人叉车3d激光slam多房间建图定位异常处理方案-墙体画线地图切分方案

墙体画线地图切分方案 针对问题:墙体两侧特征混淆误匹配,导致建图和定位偏差,表现为过门跳变、外月台走歪等 ·解决思路:预期的根治方案IGICP需要较长时间完成上线,先使用切分地图的工程化方案,即墙体两侧切分为不同地图,在某一侧只使用该侧地图进行定位 方案思路 切分原理:切分地图基于关键帧位置,而非点云。 理论基础:光照是直线的,一帧点云必定只能照射到墙的一侧,无法同时照到两侧实践考虑:关

【 html+css 绚丽Loading 】000046 三才归元阵

前言:哈喽,大家好,今天给大家分享html+css 绚丽Loading!并提供具体代码帮助大家深入理解,彻底掌握!创作不易,如果能帮助到大家或者给大家一些灵感和启发,欢迎收藏+关注哦 💕 目录 📚一、效果📚二、信息💡1.简介:💡2.外观描述:💡3.使用方式:💡4.战斗方式:💡5.提升:💡6.传说: 📚三、源代码,上代码,可以直接复制使用🎥效果🗂️目录✍️