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

news2024/11/26 20:37:41

在《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的token
      getToken({
        '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 = username
            this.isLoggedIn = true
            this.setToken(accessToken, refreshToken)
        },
        logout() {
            this.username = ''
            this.jwtAccessToken = null
            this.isLoggedIn = false
        },
        setToken(accessToken, refreshToken) {
            this.jwtAccessToken = accessToken
            this.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 sent
        const { url } = config
        // 指定页面访问需要JWT认证。
        if (url.indexOf('/login')!== -1) {
            return config
        }
        let jwt = useUserStore().getUserAccessToken
        config.headers.Authorization = `Bearer ${jwt}`
        return config
    },
    error => {
        // do something with request error
        console.log(error) // for debug
        return 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 sent
        const { url } = config
        // 指定页面访问需要JWT认证。
        if (url.indexOf('/login')!== -1) {
            return config
        }
        let jwt = useUserStore().getUserAccessToken
        config.headers.Authorization = `Bearer ${jwt}`
        return config
    },
    error => {
        // do something with request error
        console.log(error) // for debug
        return Promise.reject(error)
    }
)

// response interceptor
service.interceptors.response.use(
 
    response => {
        const res = response.data
        return res
    },
    async error => {
        console.log('err' + error) // for debug
        const originalRequest = error.config;
        // 授权验证失败
        if (error.response.status === 401 && originalRequest._retry!== true) {
            originalRequest._retry = true;
            // 刷新token
            let jwtRefreshToken=useUserStore().getRefreshToken
            await refreshToken({"refresh":jwtRefreshToken}).then((response) => {
                // 刷新token成功,重新请求
                let jwtToken=response.access
                useUserStore().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.msg
            ElMessage.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.coloradmin.cn/o/2204782.html

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

相关文章

ViT模型技术学习

前言 最近多模态模型特别火&#xff0c;模型也越来越小&#xff0c;MiniCPM-2.6只有8B&#xff0c;里面采用的图片编码器是SigLipViT模型&#xff0c;一起从头学习ViT和Transformer&#xff01;本文记录一下学习过程&#xff0c;所以是自上而下的写&#xff0c;从ViT拆到Trans…

Python Memcached 的工作原理

Python 解释 Memcached 的工作原理 在现代 Web 应用程序中&#xff0c;性能和响应速度是影响用户体验的关键因素。随着应用的用户数量和数据量的增加&#xff0c;数据库查询次数变得更加频繁&#xff0c;服务器负载也随之增加。如果每次请求都要通过数据库处理&#xff0c;那么…

【vue】监听table水平滚动条切换tab后还原位置

有个需求就是切换tab后&#xff0c;原先的table水平滚动条要还原位置&#xff08;如下图&#xff09;&#xff0c;先说下思路&#xff0c;大致就是 切出页面时 把滚动距离保存到Storage 中&#xff0c;切回来时在恢复 直接上代码 首先table ref指定一下ref"jtable" …

使用Python编写你的第一个算法交易程序

背景 Background ​ 最近想学习一下量化金融&#xff0c;总算在盈透投资者教育&#xff08;IBKRCampus&#xff09;板块找到一篇比较好的算法交易入门教程。我在记录实践过程后&#xff0c;翻译成中文写成此csdn博客&#xff0c;分享给大家。 ​ 如果你的英语好可以直接看原文…

秋日相邀!与 deepin 一起逛 OSCAR 开源产业大会开源市集

查看原文 10 月 16 日&#xff0c;“2024 OSCAR 开源产业大会”将在北京开幕。这场大会不仅是开源领域一年一度的盛会&#xff0c;更是一个汇集众多行业专家、技术领袖和开源爱好者的交流平台。大会将围绕开源技术的最新发展、产业应用、风险治理以及未来趋势等议题展开深入讨…

TCP连接重置,到底是怎么回事?

号主&#xff1a;老杨丨11年资深网络工程师&#xff0c;更多网工提升干货&#xff0c;请关注公众号&#xff1a;网络工程师俱乐部 上午好&#xff0c;我的网工朋友 连接建立失败并不仅仅包含无响应问题&#xff0c;还有一种常见的情况&#xff0c;即RST&#xff08;Reset&…

kubernetes中微服务部署

微服务 问&#xff1a;用控制器来完成集群的工作负载&#xff0c;那么应用如何暴漏出去&#xff1f; 答&#xff1a;需要通过微服务暴漏出去后才能被访问 Service 是一组提供相同服务的Pod对外开放的接口借助Service&#xff0c;应用可以实现服务发现和负载均衡Service 默认只…

网优学习干货:王者荣耀游戏用户体验洞察及质差识别(1)

一、课题背景 二、课题目的 针对热点游戏&#xff08;王者荣耀&#xff09;进行业务质量评估&#xff0c;并通过对端到端定界分析&#xff0c;从无线、核心网、互联网维度识别影响用户体验关键因素&#xff0c;为游戏用户的体验优化提供依据。 三、课题实施进度 王者荣耀卡顿特…

基于STM32的智能门锁

基于STM32的智能门锁 在现代家居安全领域&#xff0c;智能门锁已经成为提升居住安全和便利性的关键技术之一。本文将介绍一个基于STM32微控制器的智能门锁项目&#xff0c;该项目集成了多种模块&#xff0c;包括步进电机、矩阵键盘、OLED显示屏、蓝牙模块和RFID刷卡模块&#…

小程序项目实践(一)--项目的初始化以及前期的准备工作

目录 1.起步 1.1 uni-app 简介 1.2 开发工具 1.2.1 下载 HBuilderX 1.2.2 安装 HBuilderX 1.2.3 安装 scss/sass 编译 1.2.4 快捷键方案切换 1.2.5 修改编辑器的基本设置 1.3 新建 uni-app 项目 1.4 目录结构 1.5 把项目运行到微信开发者工具 1.6 使用 Git 管理项目 …

DDIM扩散模型相关原理

DDIM扩散模型相关原理 Denoising Diffusion Implicit Models 论文下载地址&#xff1a;https://arxiv.org/abs/2010.02502

CentOS 安装 nvm+Node.js

CentOS 安装 nvmNode.js 文章目录 CentOS 安装 nvmNode.js一、安装nvm①&#xff1a;更新系统包②&#xff1a;安装依赖③&#xff1a;安装 NVM④&#xff1a;配置 NVM⑤&#xff1a;验证安装 二、安装 Node.js①&#xff1a;查看可安装版本②&#xff1a;安装&#xff08;指定…

腾讯广告-web转化数据api自归因对接记录

文章目录 前言一、参考文档记录二、使用步骤1.整体流程&#xff08;可以根据官方最新的文档来做&#xff09;2.关于上报代码参考3.关于uniapp网页在history模式下接收不到url参数的问题 总结 前言 项目中需要做对扫码加粉的监测&#xff0c;于是用到了腾讯广告的Web转化数据AP…

七载耕耘,全面盘点:Zabbix实战文章精华大全分享

在数字化转型的加速推进中&#xff0c;IT监控系统的稳定与高效性已成为企业IT运维战略的核心要素。Zabbix作为开源监控领域的佼佼者&#xff0c;凭借其卓越的功能集成、灵活的定制能力以及相对经济的运营成本&#xff0c;成功吸引了全球范围内众多企业的目光。 在国内&#xf…

zynq sdk裸机开发调试问题,不能进入中断问题

zynq sdk裸机开发总是时不时的就不能进入中断了, 找来找去的找不到原因. 后来我经过多次尝试. 将问题限位到非常小的范围. 只用了最普通的内部定时器, 开启中断也是偶尔会进去偶尔会进不去. 我的版本是 2018.3 后来多次尝试慢慢的摸索出经验规律, 只要修改过代码重新开始调试.就…

Math.js 基础使用:常用的数值操作

一. 引言 在进行数学计算和处理时&#xff0c;JavaScript 提供了许多强大的工具函数&#xff0c;其中最常用且广泛应用的就是 Math 对象中的函数。无论是进行简单的数值操作还是复杂的数学运算&#xff0c;Math 工具函数都能发挥出强大的作用。 本篇文章将带您探索 JavaScrip…

汽车3d动效的作用!云渲染实现3d动效

在汽车营销领域&#xff0c;3D动效技术以其独特的视觉冲击力和交互体验&#xff0c;正成为吸引消费者注意力的新利器。而云渲染技术的应用&#xff0c;更是让这些动效如虎添翼&#xff0c;实现了高效、低成本的3D视觉内容制作与分享。本文将探讨汽车3D动效的作用&#xff0c;并…

闯关leetcode——70. Climbing Stairs

大纲 题目地址内容 解题代码地址 题目 地址 https://leetcode.com/problems/climbing-stairs/description/ 内容 You are climbing a staircase. It takes n steps to reach the top. Each time you can either climb 1 or 2 steps. In how many distinct ways can you cl…

GPT 生成绘画_Java语言例子_超详细

基于spring ai &#xff1a;简化Java AI开发&#xff0c;提升效率与维护性 过去在使用Java编写AI应用时&#xff0c;主要困境在于缺乏统一的标准化封装&#xff0c;开发者需要针对不同的AI服务提供商查阅各自独立的文档并进行接口对接&#xff0c;这不仅增加了开发的工作量&am…

Halcon Tuple数组的增删查改

read_image (Image33, E:/Halcon数据/资源图片/33.png) dev_get_window (WindowHandle) dev_set_draw (margin) get_image_size (Image33, Width, Height) query_font (WindowHandle, Font) FontWithSize : Font[0]-30 set_font (WindowHandle, FontWithSize) 增加 NewTT1T2 …