vue中双token和无感刷新token的区别

 更新时间:2024年05月12日 08:36:07   作者:锋行天下  
本文主要介绍了vue中双token和无感刷新token的区别,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

为什么有这篇小作文?

最近要给自己的项目加上token自动续期,但是在网上搜的写法五花八门,有的光前端部分就写了几百行代码,我看着费劲,摸了半天也没有实现,所以决定自己造轮子

项目构成

  • 后端部分:使用golang的gin框架起的服务
  • 前端部分:vue+elementui

先说后端部分,后端逻辑相对前端简单点,关键三步

登陆接口生成双token

"github.com/dgrijalva/jwt-go"
func (this UserController) DoLogin(ctx *gin.Context) {
    username := ctx.Request.FormValue("username")
    passWord := ctx.Request.FormValue("password")
    passMd5 := middlewares.CreateMD5(passWord)
    expireTime := time.Now().Add(10 * time.Second).Unix() //token过期时间10秒,主要是测试方便
    refreshTime := time.Now().Add(20 * time.Second).Unix() //刷新的时间限制,超过20秒重新登录
    user := []modules.User{}
    err := modules.DB.Model(&modules.User{}).Where("username = ? AND password = ?", username, passMd5).Find(&user).Error
    if err != nil || len(user) == 0 {
        ctx.JSON(400, gin.H{
            "success": false,
            "message": "用户名或密码错误",
        })
    } else {
        println("expireTime", string(rune(expireTime)))
        myClaims := MyClaims{
            user.Id,
            jwt.StandardClaims{
                ExpiresAt: expireTime,
            },
        }
        myClaimsRefrrsh := MyClaims{
            user.Id,
            jwt.StandardClaims{
                ExpiresAt: refreshTime,
            },
        }
        jwtKey := []byte("lyf123456")
        tokenObj := jwt.NewWithClaims(jwt.SigningMethodHS256, myClaims)
        tokenStr, err := tokenObj.SignedString(jwtKey)
        tokenFresh := jwt.NewWithClaims(jwt.SigningMethodHS256, myClaimsRefrrsh)
        tokenStrRefresh, err2 := tokenFresh.SignedString(jwtKey)
        if err != nil && err2 != nil {
            ctx.JSON(200, gin.H{
                "message": "生成token失败",
                "success": false,
            })
        } else {
            ctx.JSON(200, gin.H{
                "message":      "登录成功",
                "success":      true,
                "token":        tokenStr,//数据请求的token
                "refreshToken": tokenStrRefresh,//刷新token用的
            })
        }
    }
}

刷新token的方法

func (this UserController) RefrshToken(ctx *gin.Context) {
    tokenData := ctx.Request.Header.Get("Authorization") //这里是个关键点,刷新token时也要带上token,不过这里是前端传的refreshToken
    if tokenData == "" {
        ctx.JSON(401, gin.H{
            "message": "token为空",
            "success": false,
        })
        ctx.Abort()
        return
    }
    tokenStr := strings.Split(tokenData, " ")[1]
    _, claims, err := middlewares.ParseToken(tokenStr)
    expireTime := time.Now().Add(10 * time.Second).Unix()
    refreshTime := time.Now().Add(20 * time.Second).Unix()
    if err != nil {
        ctx.JSON(400, gin.H{
            "success": false,
            "message": "token传入错误",
        })
    } else {
        myClaims := MyClaims{
            claims.Uid,
            jwt.StandardClaims{
                ExpiresAt: expireTime,
            },
        }
        myClaimsRefrrsh := MyClaims{
            claims.Uid,
            jwt.StandardClaims{
                ExpiresAt: refreshTime,
            },
        }
        jwtKey := []byte("lyf123456")
        tokenObj := jwt.NewWithClaims(jwt.SigningMethodHS256, myClaims)
        tokenStr, err := tokenObj.SignedString(jwtKey)
        tokenFresh := jwt.NewWithClaims(jwt.SigningMethodHS256, myClaimsRefrrsh)
        tokenStrRefresh, err2 := tokenFresh.SignedString(jwtKey)
        if err != nil && err2 != nil {
            ctx.JSON(400, gin.H{
                "message": "生成token失败",
                "success": false,
            })
        } else {
            ctx.JSON(200, gin.H{
                "message":      "刷新token成功",
                "success":      true,
                "token":        tokenStr,
                "refreshToken": tokenStrRefresh,
            })
        }
    }
}

路由中间件里验证token

package middlewares

import (
    "strings"

    "github.com/dgrijalva/jwt-go"
    "github.com/gin-gonic/gin"
)

type MyClaims struct {
    Uid int
    jwt.StandardClaims
}

func AuthMiddleWare(c *gin.Context) {
    tokenData := c.Request.Header.Get("Authorization")
    if tokenData == "" {
        c.JSON(401, gin.H{
            "message": "token为空",
            "success": false,
        })
        c.Abort()
        return
    }
    tokenStr := strings.Split(tokenData, " ")[1]
    token, _, err := ParseToken(tokenStr)
    if err != nil || !token.Valid {
         // 这里我感觉觉是个关键点,我看别人写的,过期了返回401,但是前端的axios的响应拦截器里捕获不到,所以我用201状态码,
        c.JSON(201, gin.H{
            "message": "token已过期",
            "success": false,
        })
        c.Abort()
        return
    } else {
        c.Next()
    }
}

func ParseToken(tokenStr string) (*jwt.Token, *MyClaims, error) {
    jwtKey := []byte("lyf123456")
    // 解析token
    myClaims := &MyClaims{}
    token, err := jwt.ParseWithClaims(tokenStr, myClaims, func(token *jwt.Token) (interface{}, error) {
        return jwtKey, nil
    })
    return token, myClaims, err
}

总结一下:后端部分三步,1.登陆时生成双token,2,路由中间件里验证token,过期时返回201状态码(201是我私人定的,并不是行业标准)。3,刷新token的方法里也和登陆接口一样返回双token

前端部分

前端部分在axios封装时候加拦截器判断token是否过期,我这里跟别人写的最大的不同点是:我创建了两个axios对象,一个正常数据请求用(server),另一个专门刷新token用(serverRefreshToken),这样写的好处是省去了易错的判断逻辑

import axios from 'axios'
import { ElMessage } from 'element-plus'
import router from '../router'
//数据请求用
const server=axios.create({
  baseURL:'/shopApi',
  timeout:5000
})
// 刷新token专用
const serverRefreshToken=axios.create({
  baseURL:'/shopApi',
  timeout:5000
})
//获取新token的方法
async function getNewToken(){
  let res=await serverRefreshToken.request({
    url:`/admin/refresh`,
    method:"post",
  })
  if(res.status==200){
    sessionStorage.setItem("token",res.data.token)
    sessionStorage.setItem("refreshToken",res.data.refreshToken)
    return true
  }else{
    ElMessage.error(res.data.message)
    router.push('/login')
    return false
  }
}
//这里是正常获取数据用的请求拦截器,主要作用是给所有请求的请求头里加上token
server.interceptors.request.use(config=>{
  let token=""
  token=sessionStorage.getItem("token")
  if(token){
    config.headers.Authorization="Bearer "+token
  }
  return config
},error=>{
  Promise.reject(error)
})
//这里是正常获取数据用的响应拦截器,正常数据请求都是200状态码,当拦截到201状态码时,代表token过期了,
// 应热心小伙伴的提醒,加上防止token过期后正好短时间内多个请求重复刷新token,刷新token成功再请求
let isRefreshing=false
let refreshFnArr=[]
server.interceptors.response.use(async(res)=>{
  if(res.status==201){
    if(!isRefreshing){
    // 如果正好段时间内触发了多个请求
      isRefreshing=true
      let bl=await getNewToken()
      if(bl){
        refreshFnArr.forEach(fn=>{
          fn()
        })
        refreshFnArr=[]
        res= await server.request(res.config)
        isRefreshing=false
      }
    }else{
      return new Promise(resolve=>{
        refreshFnArr.push(
          ()=>{
            resolve(res.config)
          }
        )
      })
    }
  }
  return res
},error=>{
  if(error.response.status==500||error.response.status==401||error.response.status==400){
    router.push('/login')
    ElMessage.error(error.response.data.message)
    Promise.reject(error)
  }
  
})
//这里是刷新token专用的axios对象,他的作用是给请求加上刷新token专用的refreshToken
serverRefreshToken.interceptors.request.use(config=>{
  let token=""
  token=sessionStorage.getItem("refreshToken")
  if(token){
    config.headers.Authorization="Bearer "+token
  }
  return config
},error=>{
  Promise.reject(error)
})
export default server

总结一下,前端部分:1,正常数据请求和刷新token用的请求分开了,各司其职。省去复杂的判断。2,获取新的token和refreshToken后更新原来旧的token和refreshToken。(完结)

到此这篇关于vue中双token和无感刷新token的区别的文章就介绍到这了,更多相关vue中双token和无感刷新token内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 微信小程序scroll-view实现字幕滚动

    微信小程序scroll-view实现字幕滚动

    这篇文章主要为大家详细介绍了微信小程序scroll-view实现字幕滚动效果,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2018-07-07
  • document.write的几点使用心得

    document.write的几点使用心得

    一直用document.write()方法向浏览器中显示数据用,把它当做Alert()使用, 看来这样用有些大材小用了,下面说说它的主要用处。
    2014-05-05
  • javascript 获取元素样式必杀技

    javascript 获取元素样式必杀技

    这篇文章主要介绍了javascript 获取元素样式必杀技,需要的朋友可以参考下
    2014-05-05
  • JS for in遍历对象顺序不对解决办法

    JS for in遍历对象顺序不对解决办法

    最近使用for-in语句遍历对象属性时发现遍历顺序并非属性构建顺序,这篇文章主要给大家介绍了关于JS for in遍历对象顺序不对的解决办法,需要的朋友可以参考下
    2023-11-11
  • Js判断CSS文件加载完毕的具体实现

    Js判断CSS文件加载完毕的具体实现

    在多数情况下我们不需要判断css文件是否加载成功了,但有些时间这个功能还是需要的,今天我来整理了兼容各种浏览器的判断CSS文件加载完毕实现方法与各位分享
    2014-01-01
  • JavaScript实现节假日日历应用开发全过程

    JavaScript实现节假日日历应用开发全过程

    在Web开发中,日历控件是一个常见的组件,用于显示日期并帮助用户选择日期,这篇文章主要介绍了JavaScript实现节假日日历应用开发的相关资料,文中通过代码介绍的非常详细,需要的朋友可以参考下
    2025-10-10
  • javascript 简单抽屉效果的实现代码

    javascript 简单抽屉效果的实现代码

    javascript 简单抽屉效果的实现代码,需要的朋友可以参考下,大家自行测试。
    2010-03-03
  • JS实现图片横向滚动效果示例代码

    JS实现图片横向滚动效果示例代码

    图片横向滚动效果,大家可能都已经很熟悉了,本文为大家介绍下使用js实现横向滚动效果,喜欢的朋友可以参考下,希望对大家有所帮助
    2013-09-09
  • jszip插件实现图片打包下载的方法分析

    jszip插件实现图片打包下载的方法分析

    这篇文章主要介绍了jszip插件实现图片打包下载的方法,结合实例形式分析了JavaScript使用jszip插件依据图片列表打包下载zip压缩文件的相关操作技巧,需要的朋友可以参考下
    2023-05-05
  • 学做Bootstrap的第一个页面

    学做Bootstrap的第一个页面

    这篇文章主要为大家介绍了学做Bootstrap的第一个页面,这是学bootstrap的第一个页面,还有许多需要改进的地方,希望大家批评指正
    2016-05-05

最新评论