关于Json Web Token(token)在前后端的实践思考

news2025/2/26 4:42:26

1、前言

啥也不说了,直接进入正题,来学习一下Token在前端和后端的简单应用分析

Token是在客户端频繁向服务端请求数据,服务端频繁的去数据库查询用户名和密码进行对比,判断用户名和密码是否正确,并作出相应提示,在这样的背景下,Token便应运而生。

Token实际上就是在第一个登录的时候通过用户名和密码,在服务端验证OK后生成的一串字符串,也可以说是验证通过后服务端为其签发一个令牌,随后前端在访问服务端接口时,客户端就可以携带这个TOken令牌访问服务器,服务端只需要验证令牌的有效性即可。

下面便是请求接口的一个大致过程

  • 先登录,获取Token
  • 调用业务接口,后端要先验证Token
  • 验证OK,才继续调用业务接口返回数据
  • 验证失败,则返回给前端,比如Token过期,则重新跳转到登录

2、后端

登录接口,通过用户名和密码,或者手机号验证码的方式通过验证

public async Task<dynamic> Login([FromServices] IAuthService authService, [FromBody] FormLoginRequest loginModel)
{return await authService.login(loginModel);// authoService.login中的逻辑// 判断是否匹配,匹配成功// 创建token并写入redis,并设置超期时间// 之前业务接口调用时,直接从redis中获取// 如果有超期,返回给前端一个标识
} 

这里有一个创建Token的过程,我们来看一下token的组成

我找了一个公司正在开发项目中的token进行解析查看。主要结构如上图所示。解密以后最重要的信息便是uid,或者说是用户在后端中的唯一的用户id,那么通过uid便可以查询到相关的身份认证信息。

截图所示便是JSON Web Token的组成结构,从截图左侧仔细可以看到,中间有两个.将JWT分成了三个部分

  • HEADER
alg属性表示签名的算法(algorithm),默认是 HMAC SHA256(写成 HS256)
typ属性表示这个令牌(token)的类型(type) 
  • PAYLOAD
中间部分存放的就是实际要传输的数据 
  • Signature
Signature部分是对前面的两部分的数据进行签名,防止数据篡改。 
  • 最终生成便是
首先明确一点,是在后端生成的Token,后端会先定义一个秘钥,这个秘钥只有后端服务器才知道,
不能泄露给用户,然后使用Header中指定的签名算法(默认情况是HMAC SHA256), 
算出签名以后将Header、Payload、Signature三部分拼成一个字符串,每个部分用`.`分割开来,
就可以返给用户了。 

前端在登录认证通过获得Token并保存到前端以后,再调用业务接口的时候每次便会携带Token

后端服务会通过全局注册的环绕AOP,处理每次前端有请求到达后端的时候来对token校验

 AllowAnonymousAttribute allowAnonymousAttribute = descriptor.MethodInfo.GetCustomAttribute<AllowAnonymousAttribute>(false);// 判断可不验证token的接口if (allowAnonymousAttribute != null){await next(); return;}//获取请求头中的Authorizationstring token = context.HttpContext.Request.Headers["Authorization"];// 相当于对前端传递的token进行转换string tokenKey = "sso." + Utils.MD5(token);// redis获取,看看是否有效,直接取出返回string loginUserJson = await RedisHelper.GetAsync(tokenKey);if (!loginUserJson.IsNullOrWhiteSpace()) {RedisSSOVerifyResult resultInfo = JsonSerializer.Deserialize<RedisSSOVerifyResult>(loginUserJson);if(resultInfo.ExpiresAt > DateTime.now()) {loginUser = resultInfo.LoginUser;}else {RedisHelper.RemoveAsync(tokenKey); // 无效了 从redis中移除throw new ValidException("Token认证过期,请重新登录", -2);// 这里用-2跟前端做好约定}} else {throw new ValidException("Token认证过期,请重新登录", -2);// 这里用-2跟前端做好约定} 

大致的一个token认证过程是这样的,实际项目中相对来说还是比较复杂的,这是我从公司项目中扣取出来的。还有很多代码没有列出来,要不然会显得比较臃肿,而且主要逻辑不容易查看。

3、前端

通过登录页面,输入登录名和密码,或者手机号和验证码,获取到token,现将token存储到localStorage中,再通过token获取其他业务接口的数据。 通常可能首先通过token获取个人信息或者一些权限数据(这里只是提一下)。

 const adminLogin = async () => {// state.loading = trueconst res = await loginByMobile({mobile: state.loginForm.phone,captchaValue: state.loginForm.verificationCode,});state.loading = false;if (res?.code === 200) {localStorage.setItem("token",JSON.stringify({...res.data,account: state.loginForm.phone,}));store.dispatch("fetchMenu");}}; 

我这里登录完,直接通过token来获取当前登录用户的个人信息以及后台勾选的菜单权限,后端分别通过两个接口进行的数据返回。

 async fetchMenu({ commit }) {try {const information = await getMyInformation()if (information?.code === 200) {console.log(information, 'information')commit("setMyInformation" , information.data)const res = await getMyMenu()if(res?.code === 200) {commit("changeMenuList",res.data)window.location.href = "/"}}} catch (error) {}}, 

这里是axios针对每次的请求添加请求头的Authorization

instance.interceptors.request.use((request) => {const token = localStorage.token? JSON.parse(localStorage.token): {};request.headers = {"Authorization": token.authorization || '',"Content-Type": "application/x-www-form-urlencoded","Content-Type": "application/json",};return request;},(error) => Promise.reject(error)
); 

这里是针对后端接口返回数据的判断处理,其中有一个-2的特殊判断,这里是跟后端返回一起约定的code

instance.interceptors.response.use((response) => {// tokenif (response.data.code === -2) { // token失效ElMessage({message: "身份认证无效,请重新登录",type: "warning",});// localStorage.clear();clear()window.location.href = "/";return false;}if (response.data.code !== 200) {return Promise.reject(new Error(response.data.message));}/// ..... 其他的逻辑判断return response.data;},
} 

上面通过 code为-2 进行判断 ,然后清除掉缓存数据,那么在vue-router路由中会进行判断处理

router.beforeEach((to, _from, next) => {NProgress.start()if (to.path === '/login' || to.path === '/init-password' ||to.path === '/login-cellphone') {next()return false;}if (!localStorage.getItem('token')) {next('/login')return false}if (to.name) {next()return false}if (childrenPath.some((item) => to.path.includes(item))) {next()console.log('child');return false}// 如果找不到路由跳转到404next("/404")return false
}) 

最后

最近还整理一份JavaScript与ES的笔记,一共25个重要的知识点,对每个知识点都进行了讲解和分析。能帮你快速掌握JavaScript与ES的相关知识,提升工作效率。



有需要的小伙伴,可以点击下方卡片领取,无偿分享

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/197681.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

华为机试题:HJ37 统计每个月兔子的总数(python)

文章目录博主精品专栏导航知识点详解1、input()&#xff1a;获取控制台&#xff08;任意形式&#xff09;的输入。输出均为字符串类型。1.1、input()与list(input())的区别、及其相互转换方法2、print() &#xff1a;打印输出。3、整型int() &#xff1a;将字符串或数字转换为整…

C语言基础(二)—— 常量与变量、数据类型、进位制、关键字、原码反码补码、限定符、字符串格式化输入输出

1. 常量与变量1.1 关键字1.2 数据类型数据类型的作用&#xff1a;编译器预算对象&#xff08;变量&#xff09;分配的内存空间大小。1.3 常量在程序运行过程中&#xff0c;其值不能被改变的量常量一般出现在表达式或赋值语句中整型常量100&#xff0c;200&#xff0c;-100&…

MySQL事务篇

目录​​​​​​​ 一.事务有哪些特性&#xff1f; 二.并行事务会引发什么问题&#xff1f; 脏读 不可重复读 幻读 三.事务的隔离级别有哪些&#xff1f; 一.事务有哪些特性&#xff1f; 原子性&#xff08;Atomicity&#xff09;&#xff1a;一个事务中的所有操作&…

4.数据库安全性

学习过程参考&#xff08;后续章节同&#xff09; 【公开课】数据库系统概论&#xff08;王珊老师&#xff09;&#xff08;完结&#xff09; 《数据库系统概论》思维导图 【专栏必读】数据库系统概论第五版&#xff08;王珊&#xff09;专栏学习笔记目录导航及课后习题答案详…

2023年02月IDE流行度最新排名

点击查看最新IDE流行度最新排名&#xff08;每月更新&#xff09; 2023年02月IDE流行度最新排名 顶级IDE排名是通过分析在谷歌上搜索IDE下载页面的频率而创建的 一个IDE被搜索的次数越多&#xff0c;这个IDE就被认为越受欢迎。原始数据来自谷歌Trends 如果您相信集体智慧&am…

MySQL的函数

目录 一.分类 聚合函数 概述 格式 操作 数学函数 操作1 操作2 操作3 字符串函数 操作1 操作2 操作3 操作4 日期函数 操作1 操作2 操作3 控制流函数 if逻辑判断语句 case when 语句 窗口函数 介绍 分类 序号函数 开窗聚合函数- SUM,AVG,MIN,MAX 分布函数-…

Java 对象拷贝与转换-org.mapstruct:mapstruct 包(@Mapper、@Mapping)的使用

MapStruct的使用 最近在学习技术时候&#xff0c;发现一个特别好用的包&#xff0c;org.mapstruct:mapstruct&#xff0c;它是专门用来处理 domin 实体类与 model 类的属性映射的 它的优势&#xff1a; 很多项目大量映射的方式通过手动get、set&#xff0c;首先写法很low&…

并发编程 · 基础篇 · android线程那些事

小木箱成长营并发编程系列教程(排期中): 并发编程 基础篇(下) android线程池那些事 并发编程 提高篇(上) Java并发关键字那些事 并发编程 提高篇(下) Java锁安全性那些事 并发编程 高级篇(上) Java内存模型那些事 并发编程 高级篇(下) Java并发BATJ面试之谈 并发编程…

Jupyter notebook——在Anaconda中多个环境下,设置不同的默认打开路径

项目背景&#xff1a;anaconda中搭建了一个python3.6&#xff0c;一个python3.7版本&#xff0c;python3.6环境版本的jupyter notebook默认打开路径设置为&#xff1a;D:\DeepLearning\cv&#xff0c;修改jupyter notebook默认路径见&#xff1a;https://blog.csdn.net/qq_1881…

分享115个图片切换JS特效,总有一款适合您

分享115个图片切换JS特效&#xff0c;总有一款适合您 115个图片切换JS特效下载链接&#xff1a;https://pan.baidu.com/s/1QX7b5LDlY6lBqMVjgBKSwA?pwdk05d 提取码&#xff1a;k05d Python采集代码下载链接&#xff1a;https://wwgn.lanzoul.com/iKGwb0kye3wj jQuery多图…

Appium+Python+pytest自动化测试框架

先简单介绍一下目录&#xff0c;再贴一些代码&#xff0c;代码里有注释Basic目录下写的是一些公共的方法&#xff0c;Data目录下写的是测试数据&#xff0c;image存的是测试失败截图&#xff0c;Log日志文件&#xff0c;Page测试的定位元素&#xff0c;report测试报告&#xff…

Linux从入门到精通

Linux从入门到精通 1. Linux 简介 Linux 内核最初只是由芬兰人林纳斯托瓦兹&#xff08;Linus Torvalds&#xff09;在赫尔辛基大学上学时出于个人爱好而编写的。 Linux 是一套免费使用和自由传播的类 Unix 操作系统&#xff0c;是一个基于 POSIX&#xff08;可移植操作系统接…

Nacos 注册监听器

文章目录前言项目文件说明pom依赖bootstrap.ymlNacosConfig 配置类监听器实现类-默认实现监听器实现类-json配置处理注册监听器监听器的效果前言 本文主要讨论Nacos作为配置中心时&#xff0c;其中配置内容发生更改时&#xff0c;我们的应用程序能够做的事。 一般使用监听器来…

DeepLabV3+:Mobilenetv2的改进以及浅层特征和深层特征的融合

目录 Mobilenetv2的改进 浅层特征和深层特征的融合 完整代码 参考资料 Mobilenetv2的改进 在DeeplabV3当中&#xff0c;一般不会5次下采样&#xff0c;可选的有3次下采样和4次下采样。因为要进行五次下采样的话会损失较多的信息。 在这里mobilenetv2会从之前写好的模块中…

第四天链表

24. 两两交换链表中的节点力扣题目链接(opens new window)给定一个链表&#xff0c;两两交换其中相邻的节点&#xff0c;并返回交换后的链表。你不能只是单纯的改变节点内部的值&#xff0c;而是需要实际的进行节点交换。接下来就是交换相邻两个元素了&#xff0c;此时一定要画…

vite --- 为什么选Vite

目录 什么是Vite 为什么选Vite 现实问题 为什么生产环境仍需打包 Vite 与竞品 什么是Vite Vite&#xff08;法语意为 "快速的"&#xff0c;发音 /vit/&#xff0c;发音同 "veet"&#xff09;是一种新型前端构建工具&#xff0c;能够显著提升前端开发体…

SpringBoot+Vue图书馆管理系统1.0

简介&#xff1a;本项目采用了基本的SpringBootVue设计的图书馆管理系统。详情请看截图。经测试&#xff0c;本项目正常运行。本项目适用于Java毕业设计、课程设计学习参考等用途。 项目描述 项目名称SpringBootVue图书馆管理系统1.0源码作者LHL项目类型Java EE项目 &#xff…

Zebec 上线投票治理机制,全新流支付生态正在起航

随着加密货币的兴起&#xff0c;其除了成为一种备受关注的投资品外&#xff0c;它也正在成为一种新兴的支付手段。虽然在加密行业发展早期&#xff0c;以BTC、LTC等为代表的以支付为定位老牌加密资产&#xff0c;因支付效率低下、支付成本高、合规等问题而没能实现早期的愿景&a…

Node.js:CommonJS模块化规范

CommonJS 上文提到了 Node 采用的模块化规范是 CommonJS&#xff0c;它主要规定了如何定义模块&#xff0c;如果导出模块和如何导入模块&#xff1a; 定义模块&#xff1a;一个文件就是一个模块导出模块&#xff1a;通过 module.exports 导出模块导入模块&#xff1a;通过 re…

【Linux】第八部分 Linux常用基本命令

【Linux】第八部分 Linux常用基本命令 文章目录【Linux】第八部分 Linux常用基本命令8. Linux常用基本命令8.1 帮助命令8.2 文件目录类命令pwd 显示当前工作目录的绝对路径cd 切换目录ls 列出目录的内容mkdir 创建目录rmdir 删除目录touch 创建文件cp 复制文件或者目录rm 删除文…