node后端+vue前端实现接口请求时携带authorization验证
node后端+vue前端实现接口请求时携带authorization验证
我们在写web项目时,后端写好接口,前端想要调用后端接口时,除了登录注册页面,所有的请求都需要携带authorization,这样是为了避免随意通过接口调取数据的现象发生。这是写web项目时最基础的点,但是也挺麻烦的,涉及前后端好几个地方的编码,经常忘记怎么写的,现在记录一下。
总体流程如下:
- 后端使用中间件开启接口请求验证,除登录/注册外所有接口的请求都需要携带验证参数才能正确发起请求
- 前端登录时,存储验证消息,也就是token
- 请求拦截器中设置请求头,写入authorization
大体就这么几个步骤,下面细化
一、后端开启接口请求验证
我是用node写的后端,请求验证写在后端入口程序app.js中,完整代码如下:
const express = require("express");
const cors = require("cors");
const bodyParser = require("body-parser");
const multer = require("multer");
const upload = multer({ dest: "./public/upload" });
const morgan = require("morgan");
const fs = require("fs");const app = express();// 创建一个写入流,将日志写入access.log文件
const accessLogStream = fs.createWriteStream("./access.log", { flags: "a" });// 使用Morgan中间件,将日志写入控制台和文件
app.use(morgan("combined", { stream: accessLogStream }));app.use(cors());app.use(express.urlencoded({ extended: false }));
app.use(bodyParser.json());// 托管静态文件
app.use(upload.any());
app.use(express.static("./public"));// 处理错误的中间件
app.use((req, res, next) => {res.cc = (err, status = 1) => {res.send({status,message: err instanceof Error ? err.message : err,});};next();
});const jwtconfig = require("./jwt_config/index");
const { expressjwt: jwt } = require("express-jwt");
app.use(jwt({secret: jwtconfig.jwtSecretKey,algorithms: ["HS256"],}).unless({path: [/^\/api\/user\/.*$/],})
);const userManagerRouter = require("./router/user");
app.use("/api/user", userManagerRouter);const userInfoManageRouter = require("./router/userinfo");
app.use("/api/userinfo", userInfoManageRouter);const settingRouter = require("./router/setting");
app.use("/api/setting", settingRouter);const productRouter = require("./router/product");
app.use("/api/product", productRouter);const messageRouter = require("./router/message");
app.use("/api/message", messageRouter);const filesRouter = require("./router/files");
app.use("/api/files", filesRouter);const logRouter = require("./router/log");
app.use("/api/log", logRouter);const overviewRouter = require('./router/overview')
app.use('/api/overview', overviewRouter)// 用户消息读取情况
const dmMsgRouter = require('./router/department_msg')
app.use('/api/dm', dmMsgRouter)// 对不符合joi规则的情况进行报错
// app.use((err, req, res, next) => {
// if (err instanceof Joi.ValidationError) return res.cc(err.details[0].message);
// else res.cc(err);
// });app.listen(3088, () => {console.log("api server running at http://127.0.0.1:3088");
});
这个程序太长,相关的代码如下:
const jwtconfig = require("./jwt_config/index");
const { expressjwt: jwt } = require("express-jwt");
app.use(jwt({secret: jwtconfig.jwtSecretKey,algorithms: ["HS256"],}).unless({path: [/^\/api\/user\/.*$/],})
);
其实这个写法相对来说是固定的,首先,导入自己写好的jwt验证规则(也叫秘钥),其实就是一个jwtSecretKey,./jwt_config目录下的index.js文件如下:
module.exports = {jwtSecretKey: 'xxx' // 改成自己的秘钥
}
然后导入express-jwt,接下来就是使用中间件来设定接口路由规则了,unless方法里面写的是排除的接口地址,是用正则表达式来排除的,/^\/api\/user\/.*$/这个正则表达式的意思是排除所有以/api/user/开头的接口
可以看上面的完整代码,app.use("/api/user", userManagerRouter);这里以/api/user/开头的接口都是给用户登录和注册相关的接口
后端按这个思路写就行了
二、登录存储token
这里有两种存储方式,一种是把token存储在localstorage中,另外一种是存储在全局数据管理工具中(也就是vuex或者pinia中),这里设计前后端联调
1、后端写登录接口,向前端传递token
先看看我的完整的登录接口处理函数
exports.login = (req, res) => {// res.send("login");const userInfo = req.body;const sql = "select * from users where account = ?";db.query(sql, userInfo.account, (err, results) => {if (err) return res.cc(err);if (results.length !== 1) return res.cc("用户不存在");const compareResult = bcrypt.compareSync(userInfo.password,results[0].password);if (!compareResult) return res.cc("密码错误");// 判断账号是否冻结if (results[0].status == 1) return res.cc("账号被冻结");const user = {...results[0],password: "",imageUrl: "",create_time: "",update_time: "",};const tokenStr = jwt.sign(user, jwt_config.jwtSecretKey, {expiresIn: "10h",});res.send({status: 0,results: results[0],message: '登录成功',token: "Bearer " + tokenStr})});
};
相关的代码如下:
const tokenStr = jwt.sign(user, jwt_config.jwtSecretKey, {expiresIn: "10h",
});
res.send({status: 0,results: results[0],message: '登录成功',token: "Bearer " + tokenStr
})
这里其实很简单,就是后端通过秘钥生成一个有效期为10小时的token,这里的秘钥也是上面提到的,然后向前端发送这个token
2、前端登录时,存储token
我用的vue3,数据存储在pinia中,看看我的前端登录代码
import { useUserStore } from '@/stores/user'
const userStore = useUserStore()const loginCB = async () => {const { account, password } = form.valueconst data = { account, password }loginFormRef.value.validate(async valid => {if (valid) {try {await userStore.getUserInfo(data)// console.log(userStore.userInfo)if (!userStore.userInfo.token) return ElMessage.error('用户名或密码错误')// console.log(userStore.vue3ManageUserInfo)router.push('/')// console.log(results)} catch (error) {ElMessage.error('用户名或密码错误')console.log(error)}} else {ElMessage.error('没通过校验')}})
}
上面这段代码不是完整的,loginCB是登录按钮的回调函数,从回调中看到,其实我的登录是写在pinia的getUserInfo方法中的,继续看看这个store中的写法
import { ref } from "vue";
import { defineStore } from "pinia";
import { loginAPI } from "@/apis/user";
import { getUserInfoAPI } from "@/apis/userinfo";
import { loginLogAPI } from "@/apis/log";export const useUserStore = defineStore("user",() => {const userInfo = ref({});const getUserInfo = async (data) => {const res = await loginAPI(data);// console.log(res);userInfo.value = {account: res.results.account,token: res.token,avatar: res.results.image_url,id: res.results.id,name: res.results.name,sex: res.results.sex,email: res.results.email,department: res.results.department,identity: res.results.identity};// 登录日志await loginLogAPI({account: res.results.account,name: res.results.name,email: res.results.email,});// console.log(userInfo.value)};// 修改头像const changeAvatar = (url) => {userInfo.avatar = url;};// 修改姓名const changeName = (name) => {userInfo.name = name;};// 解决刷新页面丢失store信息的问题const clearUserInfo = () => {userInfo.value = {};};return {userInfo,getUserInfo,clearUserInfo,changeAvatar,changeName,};},{persist: true,}
);
pinia中我用的是组合式api的写法,其实也是比较流行的写法,逻辑和语法都比较清楚
可以看到,我定义了一个userInfo的state,登录需要调用loginAPI,这个接口对应后端的login函数,请求到数据后,将接口返回的token写入到userInfo.token中,这样,全局数据管理store中就存储了一个包含token的名为userInfo的state(真的绕。。。)
注意,组合式api需要将state return出去
{persist: true,}
上面这个是持久化存储,我在“vue3+pinia用户信息持久缓存(token)的问题”这篇博客中有记录,目的是将userInfo存储到localstorage中
这样,登录时要做的工作就做完了
三、请求拦截器中携带token
使用axios发起接口请求,对axios进行二次封装,封装代码如下:
import axios from "axios";
import { ElMessage } from "element-plus";
import { useUserStore } from "@/stores/user";// 创建axios实例
const http = axios.create({baseURL: "http://127.0.0.1:xxxx/api/", // 改成自己的端口timeout: 5000,
});// axios请求拦截器
http.interceptors.request.use((config) => {const userStore = useUserStore();const token = userStore.userInfo.token;if (token) {config.headers.Authorization = token;}return config;},(e) => Promise.reject(e)
);// axios响应式拦截器
http.interceptors.response.use((res) => res.data,(e) => {ElMessage.warning("接口响应出错");console.log(e);return Promise.reject(e);}
);export default http;
注意看请求拦截器中的代码,用到了刚刚上面提到的pinia中的userInfo这个state,首先获取userInfo中的token,判断是否存在token,存在的话,就把它写到请求头中去,关键的代码就是下面这行:
config.headers.Authorization = token;
这样,每次向后端发起数据请求的时候,都会携带这个token了(除了登录和注册,因为不存在),后端的中间件也能通过验证了
相关文章:
node后端+vue前端实现接口请求时携带authorization验证
node后端vue前端实现接口请求时携带authorization验证 我们在写web项目时,后端写好接口,前端想要调用后端接口时,除了登录注册页面,所有的请求都需要携带authorization,这样是为了避免随意通过接口调取数据的现象发生…...
SourceTree管理git
SourceTree管理git...
【数模百科】一篇文章讲清楚灰色预测模型GM(1,1)附python代码
本篇文章摘录自GM(1,1) - 数模百科 ,如果想了解更多有关灰色预测模型的信息,请移步 灰色预测模型 - 数模百科 首先,“灰色”这个词在这里不是指颜色,而是形容一种信息状态,介于黑(信息全无)和白…...
openssl3.2 - 官方demo学习 - mac - hmac-sha512.c
文章目录 openssl3.2 - 官方demo学习 - mac - hmac-sha512.c概述笔记END openssl3.2 - 官方demo学习 - mac - hmac-sha512.c 概述 MAC算法为HMAC, 设置参数(摘要算法为SHA3-512), 用key初始化, 对明文做MAC数据. 笔记 /*! \file hmac-sha512.c \note openssl3.2 - 官方demo…...
pycharm的使用技巧
1.新建文件时,自动生成代码 settings->editor->file and code templates,选择python script ${NAME} 文件名 ${DATE} 日期 2.自动补齐自定义段落 settings->editor->live templates,在右侧点击+号,添加自定义的内容 完成之后,在下方勾选python 3.修改注释的…...
如何通过内网穿透实现公网访问Portainer管理监控Docker容器
文章目录 前言1. 部署Portainer2. 本地访问Portainer3. Linux 安装cpolar4. 配置Portainer 公网访问地址5. 公网远程访问Portainer6. 固定Portainer公网地址 正文开始前给大家推荐个网站,前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风…...
Redis原理篇(Dict的收缩扩容机制和渐进式rehash)
Dict(即字典) Redis是一种键值型数据库,其中键与值的映射关系就是Dict实现的。 Dict通过三部分组成:哈希表(DictHashTable),哈希节点(DictEntry),字典(Dict)…...
Microsoft Remote Desktop for Mac 中文正式版下载 微软远程连接软件
Microsoft Remote Desktop 是一款专为 Mac 用户设计的远程桌面工具,它可以帮助用户通过网络连接到其他计算机,实现远程控制和操作。 软件下载:Microsoft Remote Desktop for Mac 中文正式版下载 该工具支持多种远程连接协议,包括 …...
解读Vue的原型及原型链
在 JavaScript 中,每个对象都有一个关联的原型(prototype)。原型是一个对象,其他对象可以通过原型实现属性和方法的继承。原型链是一种由对象组成的链式结构,它通过原型的引用连接了一系列对象,形成了一种继…...
拓扑排序(优先队列)queue、C++
N个小朋友,编号 1∼N,要排成一队。在安排每个人的顺序时,有 M 个要求,每个要求包含两个整数 a,b,表示小朋友 a 要排在小朋友 b 的前面。 请你找出符合所有要求的排队顺序。 输入格式 第一行包含整数 N,M。接下来 M 行…...
【Spring】SpringBoot 统一功能处理
文章目录 前言1. 拦截器1.1 什么是拦截器1.2 拦截器的使用1.2.1 自定义拦截器1.2.2 注册配置拦截器 1.3 拦截器详解1.3.1 拦截路径1.3.2 拦截器执行流程1.3.3 适配器模式 2. 统一数据返回格式3. 统一异常处理 前言 在日常使用 Spring 框架进行开发的时候,对于一些板…...
拦截器HandlerInterceptor | springmvc系列
拦截器,通俗来来将,就是我们将访问某个路径的请求给拦截下来,然后可以对这个请求做一些操作 基本使用 创建拦截器类 让类实现HandlerInterceptor接口,重写接口中的三个方法。 Component //定义拦截器类,实现Handle…...
【SQL server】DML触发器监控数据库字段值改变
文章目录 前言DML触发器基本思路创建触发器固定字段触发示例完整示例代码变量声明查询新旧值插入数据到日志表效果视频动态字段触发示例完整代码示例触发器基本信息变量声明定义游标打开游标临时表创建循环处理字段...
Docker容器(二)安装与初体验wordpress
一、安装 1.1关闭SeLinux SeLinux(Security-Enhanced Linux)是一种基于Linux内核的安全模块,旨在提供更严格的访问控制和安全策略。它通过强制实施安全策略来限制系统资源的访问,从而保护系统免受恶意软件和未经授权的访问。 在…...
Odrive 学习系列二:将烧录工具从ST-Link V2修改为JLink
一、背景: 通过观察odrive解压后的内容,可以看到在下面配置文件及makefile文件中的配置设置的均为openOCD + stlink v2,例如makefile中: # This is only a stub for various commands. # Tup is used for the actual compilation.BUILD_DIR = build FIRMWARE = $(BUILD_DI…...
ffmpeg api-codec-param-test.c源码讲解
try_decode_video_frame /*** 尝试解码视频帧** param codec_ctx 解码器上下文* param pkt 待解码的视频数据包* param decode 是否解码标志,如果为1,则进行解码,如果为0,则不解码* return 返回0表示成功,否则表示出错…...
Hive学习(14)json解析get_json_object()函数
一、语法 目的:在一个标准JSON字符串中,按照指定方式抽取指定的字符串。 string get_json_object(string <json>, string <path>) 参数说明 json:必填。STRING类型。标准的JSON格式对象,格式为{Key:Value, Key:Val…...
sqlilabs第五十五五十六关
Less-55(GET - challenge - Union- 14 queries allowed -Variation 2) 手工注入 结束 自动注入 想到一个办法能绕过需要用到IP池就可以(但是我没有) Less-56(GET - challenge - Union- 14 queries allowed -Variation 3) 手工注入...
Vue2 实现带输入的动态表格,限制el-input输入位数以及输入规则(负数、小数、整数)
Vue2 实现el-input带输入限制的动态表格,限制输入位数以及输入规则(负数、小数、整数) 在这个 Vue2 项目中,我们实现一个限制输入位数(整数16位,小数10位)以及输入规则(负数、小数、…...
反爬虫策略:使用FastAPI限制接口访问速率
目录 引言 一、网络爬虫的威胁 二、FastAPI 简介 三、反爬虫策略 四、具体实现 五、其他反爬虫策略 六、总结 引言 在当今的数字时代,数据已经成为了一种宝贵的资源。无论是商业决策、科学研究还是日常生活,我们都需要从大量的数据中获取有价值的…...
网络编程(Modbus进阶)
思维导图 Modbus RTU(先学一点理论) 概念 Modbus RTU 是工业自动化领域 最广泛应用的串行通信协议,由 Modicon 公司(现施耐德电气)于 1979 年推出。它以 高效率、强健性、易实现的特点成为工业控制系统的通信标准。 包…...
Chapter03-Authentication vulnerabilities
文章目录 1. 身份验证简介1.1 What is authentication1.2 difference between authentication and authorization1.3 身份验证机制失效的原因1.4 身份验证机制失效的影响 2. 基于登录功能的漏洞2.1 密码爆破2.2 用户名枚举2.3 有缺陷的暴力破解防护2.3.1 如果用户登录尝试失败次…...
高频面试之3Zookeeper
高频面试之3Zookeeper 文章目录 高频面试之3Zookeeper3.1 常用命令3.2 选举机制3.3 Zookeeper符合法则中哪两个?3.4 Zookeeper脑裂3.5 Zookeeper用来干嘛了 3.1 常用命令 ls、get、create、delete、deleteall3.2 选举机制 半数机制(过半机制࿰…...
【论文笔记】若干矿井粉尘检测算法概述
总的来说,传统机器学习、传统机器学习与深度学习的结合、LSTM等算法所需要的数据集来源于矿井传感器测量的粉尘浓度,通过建立回归模型来预测未来矿井的粉尘浓度。传统机器学习算法性能易受数据中极端值的影响。YOLO等计算机视觉算法所需要的数据集来源于…...
ardupilot 开发环境eclipse 中import 缺少C++
目录 文章目录 目录摘要1.修复过程摘要 本节主要解决ardupilot 开发环境eclipse 中import 缺少C++,无法导入ardupilot代码,会引起查看不方便的问题。如下图所示 1.修复过程 0.安装ubuntu 软件中自带的eclipse 1.打开eclipse—Help—install new software 2.在 Work with中…...
dify打造数据可视化图表
一、概述 在日常工作和学习中,我们经常需要和数据打交道。无论是分析报告、项目展示,还是简单的数据洞察,一个清晰直观的图表,往往能胜过千言万语。 一款能让数据可视化变得超级简单的 MCP Server,由蚂蚁集团 AntV 团队…...
Unsafe Fileupload篇补充-木马的详细教程与木马分享(中国蚁剑方式)
在之前的皮卡丘靶场第九期Unsafe Fileupload篇中我们学习了木马的原理并且学了一个简单的木马文件 本期内容是为了更好的为大家解释木马(服务器方面的)的原理,连接,以及各种木马及连接工具的分享 文件木马:https://w…...
Redis的发布订阅模式与专业的 MQ(如 Kafka, RabbitMQ)相比,优缺点是什么?适用于哪些场景?
Redis 的发布订阅(Pub/Sub)模式与专业的 MQ(Message Queue)如 Kafka、RabbitMQ 进行比较,核心的权衡点在于:简单与速度 vs. 可靠与功能。 下面我们详细展开对比。 Redis Pub/Sub 的核心特点 它是一个发后…...
阿里云Ubuntu 22.04 64位搭建Flask流程(亲测)
cd /home 进入home盘 安装虚拟环境: 1、安装virtualenv pip install virtualenv 2.创建新的虚拟环境: virtualenv myenv 3、激活虚拟环境(激活环境可以在当前环境下安装包) source myenv/bin/activate 此时,终端…...
Vue3 PC端 UI组件库我更推荐Naive UI
一、Vue3生态现状与UI库选择的重要性 随着Vue3的稳定发布和Composition API的广泛采用,前端开发者面临着UI组件库的重新选择。一个好的UI库不仅能提升开发效率,还能确保项目的长期可维护性。本文将对比三大主流Vue3 UI库(Naive UI、Element …...
