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

embedded/2024/12/23 4:51:50/

在《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/embedded/127828.html

相关文章

设置 Notepad++ 制表符(Tab 缩进)宽度为2个空格大小

Notepad 默认的制表符宽度是 4 个空格的大小&#xff0c;一个规模比较大的代码段或者 xml 等文件&#xff0c;小屏幕打开时看到的情景真的和让人着急&#xff0c;拖来拖去&#xff01;有两种方案可以解决这种情况。 修改缩进为空格 这种我们不太推荐&#xff0c;但是有些公司…

vue项目中使用websocket

一、单文件中引入使用 <template></template> <script>export default {websocket: true, // 标志需要使用WebSocketdata () {return {ws: null}},created () {this.ws new WebSocket(ws://127.0.0.1:8000); // ws服务地址this.ws.onopen () > {// 接收…

基于头脑风暴优化的模糊PI控制系统simulink建模与仿真

目录 1.课题概述 2.系统仿真结果 3.核心程序与模型 4.系统原理简介 5.完整工程文件 1.课题概述 头脑风暴优化&#xff08;Brain Storm Optimization, BSO&#xff09;是一种受人类集体创新过程启发的群体智能算法。它通过模拟团队成员之间的信息交流和想法生成来寻找最优解…

海康威视云台相机图像获取

直接上代码&#xff1a; import cv2# 替换为正确的RTSP链接 rtsp_url rtsp://admin:abcd12345192.168.1.64:554/h264/ch1/main/av_stream cap cv2.VideoCapture(rtsp_url)if not cap.isOpened():print("无法打开视频流&#xff0c;检查RTSP URL和凭证") else:whil…

Linux便捷查询使用手册 第十章:安全与权限

目录 10.1 Linux安全模型 10.1.1 权限与拥有者 示例&#xff1a;查看文件权限 10.1.2 用户与组管理 示例&#xff1a;创建用户和组 10.1.3 文件权限管理 示例&#xff1a;更改文件权限 10.2 安全工具与技术 10.2.1 防火墙&#xff08;iptables&#xff09; 示例&…

【WebGIS】Cesium:界面与默认控件

Cesium 是一个用于构建三维地球与地图应用的开源 JavaScript 库。在开发 WebGIS 应用时&#xff0c;了解 Cesium 的界面与默认控件是非常关键的。本文将详细介绍 Cesium 的界面组成、默认控件的功能和自定义配置方法&#xff0c;帮助你快速上手。 Cesium Viewer 界面概述 Ces…

javaWeb项目-ssm+jsp企业人力资源管理系统功能介绍

本项目源码&#xff08;点击下方链接下载&#xff09;&#xff1a; java-ssmjsp中小企业人力资源管理系统实现源码(项目源码-说明文档)资源-CSDN文库 项目关键技术 开发工具&#xff1a;IDEA 、Eclipse 编程语言: Java 数据库: MySQL5.7 框架&#xff1a;ssm、Springboot 前端…

Qt5.14.2 安装详细教程(图文版)

Qt 是一个跨平台的 C 应用程序开发框架&#xff0c;主要用于开发图形用户界面&#xff08;GUI&#xff09;程序&#xff0c;但也支持非 GUI 程序的开发。Qt 提供了丰富的功能库和工具&#xff0c;使开发者能够在不同平台上编写、编译和运行应用程序&#xff0c;而无需修改代码。…