忘记密码找回流程请求拦截器-前端

目录

设置找回密码请求拦截器

1.相关参数

2.约定

代码实现

1. 实现思路

2. 实现代码


校园统一身份认证系统:

基于网络安全,找回密码、重新设置密码的流程和正常登录流程中密钥等请求头不一致。

设置找回密码请求拦截器

1.相关参数

  • clientId  应用ID,在点击忘记密码跳转后携带,如下所示:
http://192.168.31.134:8080/? clientId = webApp &secretKey = 3dtii4jr91iz8ypj &secretValue = ecmo5dro13as5y79
  • secretKey 密钥的key,在点击忘记密码跳转后携带,如下所示:
     
http://192.168.31.134:8080/ ?
clientId = webApp &secretKey = 3dtii4jr91iz8ypj &secretValue = ecmo5dro13as5y79
  • secretValue 密钥的value,在点击忘记密码跳转后携带,如下所示:
http://192.168.31.134:8080/ ?
clientId = webApp &secretKey = 3dtii4jr91iz8ypj &secretValue = ecmo5dro13as5y79
  • nonce 随机字符串,10分钟内不要重复
  • timestamp 时间戳(毫秒),纯数字
  • sign 签名,利用上面的参数,使用SM3加密算法生成的加密字符串,加密前的明文如下所示(加密时没有用到secretKey):
nonce = "{ 随机字符串 }" ×tamp = { 时间戳 ( 毫秒 )} &clientId = "{ 应用 ID}" &secret = "
{secretValue}"
  • jwtSecret 生成jwt的密钥,值是 8w9L95DwBCD9xjkR

2.约定

从统一登录页点击忘记密码跳转时,会携带 1 个参数,如下所示:
http://192.168.31.134:8080/AccountBack ?
key = eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJjbGllbnRJZCI6IndlYkFwcCIsInNlY3JldEt
leSI6ImF2dGxuYXlldnBtbWo5NDgiLCJzZWNyZXRWYWx1ZSI6InMzZ2Jma2QzdDI2YWJ5NzQifQ.y1eT
agvFM8gPxC1k2oqZIwr_9-IhyHAhWJqkZ8CFrnI
clientId secretKey secretValue timeEnd 作为参数,以 jwtSecret 为密钥,生成的结果
就是 key ;其中 timeEnd key 有效截止时间的时间戳。
每次请求 前都需要判断 key 是否还有效,如果无效需要重新开始忘记密码的流程。
每次请求 都需要以 clientId timestamp , nonce , secretKey , sign 5 个值为参数,以 jwtSecret
为密钥生成 key ;并将结果放入请求头的 key 参数中。
:
1. 生成 key 时需要的是 secretKey ,而不是 secretValue。

代码实现

1. 实现思路

  1. jwtDecode对key解码。
  2. 获取数据:secretKey密钥,secretValue密钥值,timeEnd有效截至时间,redirectUri退回登录时需要携带url的标识。
  3. 获取当前时间戳检查是否超过有效截至时间。
  4. 生成签名sign、随机字符串nonce(无长度字符限制,为避免出错现在在数字和字母中,符号在解析过程中容易出错),jwt加密重新生成新的key。

2. 实现代码

使用router 文件 index.js 中的 accessToken:
const router = new VueRouter({mode: "history",base: process.env.BASE_URL,routes,
});
const VueRouterPush = VueRouter.prototype.push;
VueRouter.prototype.push = function push(to) {return VueRouterPush.call(this, to).catch((err) => err);
};router.beforeEach((to, from, next) => {const fullPath=to.fullPath.match(/\?(.*)/)if(fullPath) {const searchParams = new URLSearchParams(fullPath[1])if (searchParams.get('key')) {localStorage.setItem('accessToken', searchParams.get('key')) next(to.path)}
}else {next()
}})
export default router;

request1.js(和其他request.js不同)中:

引入文件依赖:

//创建axios实例
import axios from "axios";
// 导入 sm-crypto 库
import sm from "sm-crypto";
import CryptoJS from 'crypto-js'
import { jwtDecode } from "jwt-decode";
import { getToken, setToken, removeToken } from "@/utils/auth";
import { Notification, MessageBox, Message, Loading } from "element-ui";
import errorCode from "@/utils/errorCode";
import { eventBus } from '../../src/main';

请求拦截器:

// 请求拦截器
service.interceptors.request.use((config) => {// 在发送请求之前做些什么let accessToken = localStorage.getItem("accessToken");// console.log("key", accessToken);//解码密钥const jwtSecret = "8w9L95DwBCD9xjkR"; // 生成JWT密钥,固定数值// 解码JWT令牌获取参数// clientId secretKey secretValue timeEndconst decodedToken = jwtDecode(accessToken, jwtSecret);console.log(decodedToken);const clientId = decodedToken.clientId; //应用IDconsole.log("clientId");console.log(clientId);const secretKey = decodedToken.secretKey; //密钥const secretValue = decodedToken.secretValue; //密钥值const timeEnd = decodedToken.timeEnd; //获取key有效截止时间的时间戳const redirectUri = decodedToken.redirectUri ? decodedToken.redirectUri : null;window.redirectUri = redirectUri;window.clientId = clientId; // 存储在全局变量中// 通过事件总线发送事件通知eventBus.$emit('dataUpdated', {clientId,redirectUri});console.log("window.clientId");console.log(window.clientId);// 检查key是否有效if (timeEnd && Date.now() > Number(timeEnd)) {// key已过期,重新开始忘记密码流程return Promise.reject(); //超时,请求中断}const nonce = generateNonce(); // 生成随机字符串nonceconst timestamp = Date.now(); // 获取当前时间戳(毫秒级别)const sign = generateSign(nonce, timestamp, clientId, secretValue); // 生成签名sign// 生成加密keyconst payload = {clientId,timestamp,nonce,secretKey,sign,};const jwtToken = generateKey(payload, jwtSecret);// console.log("key");// console.log(jwtToken);config.headers["key"] = jwtToken; // 将key添加到请求头中并转换为字符串类型 // 将key添加到请求头中并转换为字符串类型return config;},(error) => {// 对请求错误做些什么return Promise.reject(error);}
);
// 生成随机字符串nonce的函数
function generateNonce() {const characters ="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";const length = 16;let nonce = "";for (let i = 0; i < length; i++) {nonce += characters.charAt(Math.floor(Math.random() * characters.length));}return nonce;
}// 生成签名sign的函数
function generateSign(nonce, timestamp, clientId, secretValue) {const plaintext = `nonce=${nonce}&timestamp=${timestamp}&clientId=${clientId}&secret=${secretValue}`;const sign = sm.sm3(plaintext); // 使用sm-crypto库中的sm3函数生成签名return sign;
}// 函数用于将对象编码为Base64格式
function encodeBase64(obj) {const stringifiedObj = JSON.stringify(obj);const wordArray = CryptoJS.enc.Utf8.parse(stringifiedObj);return CryptoJS.enc.Base64.stringify(wordArray).replace(/=+$/, "");
}// 生成JWT的函数
function generateJWT(payload, secret) {// 定义JWT的头部const header = { alg: "HS256", typ: "JWT" };// 编码头部和负载const encodedHeader = encodeBase64(header);const encodedPayload = encodeBase64(payload);// 生成签名const signature = CryptoJS.HmacSHA256(encodedHeader + "." + encodedPayload,secret);const signatureBase64 = CryptoJS.enc.Base64.stringify(signature).replace(/=+$/,"");const jwtToken = `${encodedHeader}.${encodedPayload}.${signatureBase64}`;return jwtToken;
}
// 生成key的函数
function generateKey(payload, jwtSecret) {const jwtToken = generateJWT(payload, jwtSecret);return jwtToken;
}

响应拦截器(无特殊处理):

function logOutWay() {MessageBox.confirm('未登录或登录已过期,请重新登录。', '系统提示', {confirmButtonText: '重新登录',showCancelButton: false,distinguishCancelAndClose: false,showClose: false,closeOnClickModal: false,type: 'warning'}).then(() => {// 跳转到登录页面let accessTokenKey = getToken()removeToken()localStorage.removeItem('accessToken')window.location.href = process.env.VUE_APP_BASE_JUMP+'auth/logout?accessToken=' + accessTokenKey + '&redirectUri='+process.env.VUE_APP_BASE_JUMP}).catch(() => {});}// 响应拦截器
service.interceptors.response.use((res) => {const isEncrypt = true;if (isEncrypt === "true" && !matchs(res.config.url, excludPtahs)) {//console.log("================解密===================")res.data = JSON.parse(interfaceDecryptSm2(res.data));}// console.log("拦截器===", res);// 未设置状态码则默认成功状态const code = res.data.code || 0;// 获取错误信息const msg = errorCode[code] || res.data.msg || errorCode["default"];// 二进制数据则直接返回if (res.request.responseType === "blob" ||res.request.responseType === "arraybuffer") {// console.log("1111===");return res.data;}if (code === 401) {} else if (code === 1) {if (msg && msg.includes("key不存在或已过期")) {logOutWay()// 中断请求return Promise.reject("无效的会话,或者会话已过期,请重新登录。");} else {Message({ message: msg, type: "error" });return Promise.reject(new Error(msg));}} else if (code === 601) {Message({ message: msg, type: "warning" });return Promise.reject("error");}// else if (code !== 0) {// 	Notification.error({ title: msg })// 	return Promise.reject('error')// }else {// console.log("22222===",res.data);return res.data;}},(error) => {console.log("err====" + error);let { message } = error;if (message == "Network Error") {message = "后端接口连接异常";} else if (message.includes("timeout")) {message = "系统接口请求超时";} else if (message.includes("Request failed with status code")) {if (message.substr(message.length - 3) == 401) {console.log("401走重新请求token逻辑");message = "未登录或登录已过期,请重新登录。";} else {message = "系统接口" + message.substr(message.length - 3) + "异常";}}Message({ message: message, type: "error", duration: 5 * 1000 });return Promise.reject(error);}
);//导入文件
export default service;

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【Android开发】【创建Activity,Activity之间的切换/消息传递】【java】

一、第一个Activity 1.1 创建一个空Activity 1.2 创建一个布局 知识点 在XML中引用一个id&#xff1a;id/id_name 在XML中定义一个id&#xff1a;id/id_name 右键错误&#xff0c;点击Show Quick-Fixes&#xff0c;再点击弹出的Suppress:Add........&#xff0c;错误会被自动修…

【C语言】动态内存分配

1、为什么要有动态内存分配 不管是C还是C中都会大量的使用&#xff0c;使用C/C实现数据结构的时候&#xff0c;也会使用动态内存管理。 我们已经掌握的内存开辟方式有&#xff1a; int val 20; //在栈空间上开辟四个字节 char arr[10] { 0 }; //在栈空间…

利用Base64加密算法将数据加密解密

1. Base64加密算法 Base64准确来说并不像是一种加密算法&#xff0c;而更像是一种编码标准。 我们知道现在最为流行的编码标准就是ASCLL&#xff0c;它用八个二进制位&#xff08;一个char的大小&#xff09;表示了127个字符&#xff0c;任何二进制序列都可以用这127个字符表…

【小沐学AI】智谱AI大模型的一点点学习(Python)

文章目录 1、简介1.1 大模型排行榜 2、智谱AI2.1 GLM2.1.1 模型简介2.1.2 开源代码2.1.2.1 GLM-130B 2.2 ChatGLM2.2.1 模型简介2.2.2 开源代码2.2.2.1 ChatGLM-6B2.2.2.2 ChatGLM3 2.3 CodeGeeX2.3.1 模型简介2.3.2 开源代码 2.4 CogView2.4.1 模型简介2.4.2 开源代码 2.5 Cog…

关于序列化和反序列化

什么是序列化&#xff0c;什么是反序列化 简单来说&#xff1a; 序列化&#xff1a;将数据结构或对象转换成二进制字节流的过程反序列化&#xff1a;将在序列化过程中所生成的二进制字节流转换成数据结构或者对象的过程 为什么要进行序列化 我们要将java对象进行网络传输&a…

Grok-1安装

安装 源代码 huggingface git clone https://github.com/xai-org/grok-1.git # 新建虚拟环境 conda create --prefixD:\CondaEnvs\grok1 python3.11conda activate D:\CondaEnvs\grok1 pip install huggingface_hub[hf_transfer] pip install -U "huggingface_hub[cli]&…

#Linux(Source Insight安装及工程建立)

&#xff08;一&#xff09;发行版&#xff1a;Ubuntu16.04.7 &#xff08;二&#xff09;记录&#xff1a; &#xff08;1&#xff09;安装教程&#xff1a; Source insight 工具安装及使用方法-CSDN博客https://blog.csdn.net/YAOHAIPI/article/details/125191451&#xff…

浅谈前端路由原理hash和history

1、认识前端路由 本质 前端路由的本质&#xff0c;是监听 url 地址或 hash 值的改变&#xff0c;来切换渲染对应的页面组件 前端路由分为两种模式 hash 模式 history 模式 两种模式的对比 2、hash 模式 &#xff08;1&#xff09;hash 定义 hash 模式是一种把前端路由的路…

SpringCloud之网关组件Gateway学习

SpringCloud之网关组件Gateway学习 GateWay简介 Spring Cloud Gateway是Spring Cloud的⼀个全新项目&#xff0c;目标是取代Netflix Zuul&#xff0c;它基于Spring5.0SpringBoot2.0WebFlux&#xff08;基于高性能的Reactor模式响应式通信框架Netty&#xff0c;异步⾮阻塞模型…

Wireshark TS | DNS 案例分析之外的思考

前言 承接之前一篇《Packet Challenge 之 DNS 案例分析》&#xff0c;在数据包跟踪文件 dnsing.pcapng 中&#xff0c;关于第 4 题&#xff08;What is the largest DNS response time seen in this trace file? &#xff09;的分析过程中曾经碰到一个小问题&#xff0c;主要…

Pycharm小妙招之Anaconda离线配环境

Pycharm小妙招之Anaconda离线配环境———如何给无法联网的电脑配python环境&#xff1f; 1. 预备工作2. 电脑1导出包2.1 环境路径2.2 压缩py38导出至U盘 3. 电脑2导入包4. 验证是否导入成功4.1 conda查看是否导入4.2 pycharm查看能否使用 1. 预备工作 WINDOWS系统电脑1(在线)…

设计模式及其在项目、框架中的应用

设计模式的作用&#xff1a; 1、类之间关系图&#xff0c;明确的角色及其关系、作用&#xff1b; 2、符合开闭原则&#xff0c;职责明确&#xff0c;并且开放的拓展点可以有效应对后期的变化。 &#xff08;一&#xff09;、责任链模式 适用场景&#xff1a; 在一个流程中&…