前后端分离(蜗牛学苑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

相关文章

CSS3 布局样式及其应用举例

《CSS3布局样式及其应用举例》CSS3的布局特性为前端开发者提供了无限可能,无论是Flexbox的一维布局还是Grid的二维布局,它们都能够帮助开发者以更清晰、简洁的方式实现复杂的网页布局,本文给... 目录深入探讨 css3 布局样式及其应用引言一、CSS布局的历史与发展1.1 早期布局的局限性1.2

使用animation.css库快速实现CSS3旋转动画效果

《使用animation.css库快速实现CSS3旋转动画效果》随着Web技术的不断发展,动画效果已经成为了网页设计中不可或缺的一部分,本文将深入探讨animation.css的工作原理,如何使用以及... 目录1. css3动画技术简介2. animation.css库介绍2.1 animation.cs

CSS引入方式和选择符的讲解和运用小结

《CSS引入方式和选择符的讲解和运用小结》CSS即层叠样式表,是一种用于描述网页文档(如HTML或XML)外观和格式的样式表语言,它主要用于将网页内容的呈现(外观)和结构(内容)分离,从而实现... 目录一、前言二、css 是什么三、CSS 引入方式1、行内样式2、内部样式表3、链入外部样式表四、CSS 选

使用雪花算法产生id导致前端精度缺失问题解决方案

《使用雪花算法产生id导致前端精度缺失问题解决方案》雪花算法由Twitter提出,设计目的是生成唯一的、递增的ID,下面:本文主要介绍使用雪花算法产生id导致前端精度缺失问题的解决方案,文中通过代... 目录一、问题根源二、解决方案1. 全局配置Jackson序列化规则2. 实体类必须使用Long封装类3.

SpringBoot实现接口数据加解密的三种实战方案

《SpringBoot实现接口数据加解密的三种实战方案》在金融支付、用户隐私信息传输等场景中,接口数据若以明文传输,极易被中间人攻击窃取,SpringBoot提供了多种优雅的加解密实现方案,本文将从原... 目录一、为什么需要接口数据加解密?二、核心加解密算法选择1. 对称加密(AES)2. 非对称加密(R

详解如何在SpringBoot控制器中处理用户数据

《详解如何在SpringBoot控制器中处理用户数据》在SpringBoot应用开发中,控制器(Controller)扮演着至关重要的角色,它负责接收用户请求、处理数据并返回响应,本文将深入浅出地讲解... 目录一、获取请求参数1.1 获取查询参数1.2 获取路径参数二、处理表单提交2.1 处理表单数据三、

使用Python自动化生成PPT并结合LLM生成内容的代码解析

《使用Python自动化生成PPT并结合LLM生成内容的代码解析》PowerPoint是常用的文档工具,但手动设计和排版耗时耗力,本文将展示如何通过Python自动化提取PPT样式并生成新PPT,同时... 目录核心代码解析1. 提取 PPT 样式到 jsON关键步骤:代码片段:2. 应用 JSON 样式到

SpringBoot实现二维码生成的详细步骤与完整代码

《SpringBoot实现二维码生成的详细步骤与完整代码》如今,二维码的应用场景非常广泛,从支付到信息分享,二维码都扮演着重要角色,SpringBoot是一个非常流行的Java基于Spring框架的微... 目录一、环境搭建二、创建 Spring Boot 项目三、引入二维码生成依赖四、编写二维码生成代码五

Java如何根据文件名前缀自动分组图片文件

《Java如何根据文件名前缀自动分组图片文件》一大堆文件(比如图片)堆在一个目录下,它们的命名规则遵循一定的格式,混在一起很难管理,所以本文小编就和大家介绍一下如何使用Java根据文件名前缀自动分组图... 目录需求背景分析思路实现代码输出结果知识扩展需求一大堆文件(比如图片)堆在一个目录下,它们的命名规

Java实现MinIO文件上传的加解密操作

《Java实现MinIO文件上传的加解密操作》在云存储场景中,数据安全是核心需求之一,MinIO作为高性能对象存储服务,支持通过客户端加密(CSE)在数据上传前完成加密,下面我们来看看如何通过Java... 目录一、背景与需求二、技术选型与原理1. 加密方案对比2. 核心算法选择三、完整代码实现1. 加密上