vue3实现登录获取token并自动刷新token进行JWT认证

news/2024/10/10 10:36:11/

在《django应用JWT(JSON Web Token)实战》介绍了如何通过django实现JWT,并以一个具体API接口实例的调用来说明JWT如何使用。本文介绍如何通过vue3的前端应用来使用JWT认证调用后端的API接口,实现一下的登录认证获取JWT进行接口认证。

在这里插入图片描述

一、账号密码登录获取JWT

通过Login.vue实现登录的用户名、密码表单信息收集,调用getToken()方法进行鉴权验证并获取jwt的token。
Login.vue

<template><div class="body"><el-form :model="form" :rules="rules" ref="loginForm" class="loginContainer"><h3 class="loginTitle">欢迎登录</h3><el-form-item label="用户名" prop="username" :label-width="formLabelWidth"> <el-input type="text" v-model="form.username" placeholder="请输入用户名"></el-input></el-form-item><el-form-item label="密码"  prop="password" :label-width="formLabelWidth"> <el-input type="password" v-model="form.password" placeholder="请输入密码"></el-input></el-form-item><el-form-item :label-width="formLabelWidth"> <el-button type="primary" :plain="true" @click="submitForm('form')">登录</el-button></el-form-item></el-form></div>
</template><script>
import { useUserStore } from '@/stores/user'
import { ElMessage } from 'element-plus';
import {getToken} from '../api/user'
export default {data() {return {form: {username: '',password: '',err_username: "",err_password: "",},rules: {username: [{ required: true, message: '请输入用户名', trigger: 'blur' }],password: [{ required: true, min:6, message: '请输入密码', trigger: 'blur' }]},     formLabelWidth: '120px'};},methods: {submitForm(formName) {if (this.$refs.loginForm) {this.$refs.loginForm.validate(valid => {if (valid) {// 提交表单逻辑console.log('提交成功:', this.form);this.login();} else {console.log('验证失败');ElMessage.error('验证失败,请检查您的输入!');}});} else {console.error('表单未找到');}},login() {var that = this;this.message = "";// 用户名密码鉴权获取jwt的tokengetToken({'username': this.form.username,'password': this.form.password,}).then((Response) => {console.log(Response);if (Response && Response.access) {// //保存数据到本地存储this.username= that.form.username;useUserStore().login(this.username,Response.access,Response.refresh)this.username = "";this.password = "";this.$router.push({name:"home"}); //跳转到首页}}).catch(function (error) {console.log(error);if ("username" in error) {that.err_username = error.username[0];} else if ("password" in error) {that.err_password = error.password[0];} else {ElMessage.error('登录失败!');}});},}
};
</script><style scoped>.loginContainer{border-radius: 15px;background-clip: padding-box;text-align: left;margin: auto;margin-top: 180px;width: 450px;padding: 15px 35px 15px 35px;background: aliceblue;border:1px solid blueviolet;box-shadow: 0 0 25px #f885ff;}.loginTitle{margin: 0px auto 48px auto;text-align: center;font-size: 40px;}.loginRemember{text-align: left;margin: 0px 0px 15px 0px;}.loginbody{width: 100vw;height: 100vh;background-size:100%;overflow: hidden;}
</style>

在登录时调用getToken()方法获取jwt的token
getToken的封装方法如下:

import request from '@/utils/request'export function getToken(data) {return request({url: 'token/',method: 'post',data})}

通过用户名和密码鉴权可以获得JWT的token,接口会返回access的token和refresh的token,需要将这两个token保存下来,access的token用来进行API接口的jwt认证,refresh的token用来刷新失效的access的token。

二、将JWT保存至本地

通过pinia将token保存至浏览器的本地存储,以便于后面请求API时带上访问的token

import { defineStore } from 'pinia'
import { refreshToken } from '../api/user'export const useUserStore = defineStore('user', {persist: {enabled: true, //开启数据持久化strategies: [{key: "userState", //给一个要保存的名称storage: localStorage, //sessionStorage / localStorage 存储方式},],},state: () => ({isLoggedIn: false,username: '',jwtAccessToken: null,jwtRefreshToken: null,}),actions: {login(username, accessToken,refreshToken) {this.username = usernamethis.isLoggedIn = truethis.setToken(accessToken, refreshToken)},logout() {this.username = ''this.jwtAccessToken = nullthis.isLoggedIn = false},setToken(accessToken, refreshToken) {this.jwtAccessToken = accessTokenthis.jwtRefreshToken = refreshToken},refreshToken() {return new Promise((resolve, reject) => {refreshToken({"refresh":this.jwtRefreshToken}).then((response) => {this.setToken(response.access, this.jwtRefreshToken)resolve(response.access)console.log('return refreshToken-----------'+response.access)}).catch((error) => {reject(error)})})}},getters: {getIsLoggedIn: (state) => state.isLoggedIn,getUsername: (state) => state.username,getUserAccessToken: (state) => state.jwtAccessToken,getRefreshToken: (state) => state.jwtRefreshToken,}
})

在登录的Login.vue组件中调用useUserStore().login(this.username,Response.access,Response.refresh)将用户名、access的token、refresh的token保存至浏览器的本地存储。

三、请求API带上JWT

将axios的调用封装成request.js在调用API接口时带上JWT

import axios from 'axios'
import Router from '@/components/tools/Router'
import { useUserStore } from '@/stores/user'
import { ElMessage } from 'element-plus'
import { refreshToken } from '../api/user'const api_rul = import.meta.env.VITE_APP_API_URL// create an axios instance
const service = axios.create({baseURL: api_rul,timeout: 5000, // request timeout
})// request interceptor
service.interceptors.request.use(config => {// do something before request is sentconst { url } = config// 指定页面访问需要JWT认证。if (url.indexOf('/login')!== -1) {return config}let jwt = useUserStore().getUserAccessTokenconfig.headers.Authorization = `Bearer ${jwt}`return config},error => {// do something with request errorconsole.log(error) // for debugreturn Promise.reject(error)}
)export default service

主要是在请求头重带着jwt的信息

let jwt = useUserStore().getUserAccessToken
config.headers.Authorization = `Bearer ${jwt}`

四、在token失效时自动重新获取token

前面提到JWT基于安全考虑有两个token,一个是access token ,一个是refresh token 。access token的失效时间较短,可以有效降低泄露而造成的影响,两个token的区别和作用如下:

access tokenrefresh token
有效时间较短(如半小时)较长(如一天)
作用鉴权验证重新获取access token
什么时候使用每次接口鉴权验证时access token失效时使用

使用refresh token的逻辑如下:
在这里插入图片描述

以下通过拦截器实现token失效后重新获取access token

import axios from 'axios'
import Router from '@/components/tools/Router'
import { useUserStore } from '@/stores/user'
import { ElMessage } from 'element-plus'
import { refreshToken } from '../api/user'const api_rul = import.meta.env.VITE_APP_API_URL// create an axios instance
const service = axios.create({baseURL: api_rul,timeout: 5000, // request timeout
})// request interceptor
service.interceptors.request.use(config => {// do something before request is sentconst { url } = config// 指定页面访问需要JWT认证。if (url.indexOf('/login')!== -1) {return config}let jwt = useUserStore().getUserAccessTokenconfig.headers.Authorization = `Bearer ${jwt}`return config},error => {// do something with request errorconsole.log(error) // for debugreturn Promise.reject(error)}
)// response interceptor
service.interceptors.response.use(response => {const res = response.datareturn res},async error => {console.log('err' + error) // for debugconst originalRequest = error.config;// 授权验证失败if (error.response.status === 401 && originalRequest._retry!== true) {originalRequest._retry = true;// 刷新tokenlet jwtRefreshToken=useUserStore().getRefreshTokenawait refreshToken({"refresh":jwtRefreshToken}).then((response) => {// 刷新token成功,重新请求let jwtToken=response.accessuseUserStore().setToken(jwtToken, jwtRefreshToken)console.log('return refreshToken-----------'+response.access)originalRequest.headers.Authorization = `Bearer ${jwtToken}`return service(originalRequest)                }).catch((error) => {// 刷新token失败,跳转到登录页面ElMessage.error('请重新登录!')Router.push({name:'login'})})}// 内部错误if (error.response.status === 500) {let errormsg=error.response.data.msgElMessage.error('服务器内部错误!'+errormsg)}if (error.response.status === 400){ElMessage.error('错误的请求!')}return Promise.reject(error)}
)export default service

在判断error.response.status === 401时调用refreshToken重新获取jwttoken进行接口的调用。


博客地址:http://xiejava.ishareread.com/


http://www.ppmy.cn/news/1536966.html

相关文章

【ROS】机器人系统仿真-URDF集成Rviz基本流程

机器人系统仿真&#xff1a;是通过计算机对实体机器人系统进行模拟的技术&#xff0c;在 ROS 中&#xff0c;仿真实现涉及的内容主要有三:对机器人建模(URDF)、创建仿真环境(Gazebo)以及感知环境(Rviz)等系统性实现。 1.URDF URDF可以以一种 XML 的方式描述机器人的部分结构&…

Vue.js 组件开发知识详解

✅作者简介&#xff1a;2022年博客新星 第八。热爱国学的Java后端开发者&#xff0c;修心和技术同步精进。 &#x1f34e;个人主页&#xff1a;Java Fans的博客 &#x1f34a;个人信条&#xff1a;不迁怒&#xff0c;不贰过。小知识&#xff0c;大智慧。 &#x1f49e;当前专栏…

RCE(remote command/code execute)远程命令注入

远程命令注入RCE RCE(remote command/code execute&#xff0c;远程命令执行)漏洞&#xff0c;一般出现这种漏洞&#xff0c;是因为应用系统从设计上需要给用户提供指定的远程命令操作的接口&#xff0c;比如我们常见的路由器、防火墙、入侵检测等设备的web管理界面上。一般会给…

【python机器学习】线性回归 拟合 欠拟合与过拟合 以及波士顿房价预估案例

文章目录 线性回归之波士顿房价预测案例 欠拟合与过拟合线性回归API 介绍:波士顿房价预测数据属性:机器学习代码实现 拟合 过拟合 欠拟合 模拟 及处理方法(正则化处理)导包定义函数表示欠拟合定义函数表示拟合定义函数表示过拟合 正则化处理过拟合L1正则化L2正则化 线性回归之波…

数据交换的金钟罩:合理利用安全数据交换系统,确保信息安全

政府单位为了保护网络不受外部威胁和内部误操作的影响&#xff0c;通常会进行网络隔离&#xff0c;隔离成内网和外网。安全数据交换系统是专门设计用于在不同的网络环境&#xff08;如内部不同网络&#xff0c;内部网络和外部网络&#xff09;之间安全传输数据的解决方案。 使用…

mysql事务使用和事务隔离级别与sqlserver的比较

在 MySQL 中&#xff0c;事务 (Transaction) 是一个将一组 SQL 语句作为一个整体执行的机制。事务确保要么所有操作都执行成功&#xff0c;要么在遇到错误时回滚到之前的状态&#xff0c;从而保证数据库数据的一致性和完整性。 事务的四大特性&#xff08;ACID&#xff09; 事…

【Flutter 面试题】解析 Flutter 与 Native 开发优缺点:跨平台、性能比较、生态成熟度、开发效率、原生功能支持

【Flutter 面试题】解析 Flutter 与 Native 开发优缺点:跨平台、性能比较、生态成熟度、开发效率、原生功能支持 文章目录 写在前面口述回答补充说明写在前面 🙋 关于我 ,小雨青年 👉 CSDN博客专家,GitChat专栏作者,阿里云社区专家博主,51CTO专家博主。2023博客之星T…

系统架构设计师教程 第12章 12.4 信息系统架构案例分析 笔记

12.4 信息系统架构案例分析 ★★★★☆ 12.4.1 价值驱动的体系结构——连接产品策略与体系结构 1.价值模型概述 价值模型核心的特征可以简化为三种基本形式。 (1)价值期望值&#xff1a;表示对某一特定功能的需求&#xff0c;包括功能、质量和不同 级别质量的实用性。 (2)…