翼度科技»论坛 编程开发 JavaScript 查看内容

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

7

主题

7

帖子

21

积分

新手上路

Rank: 1

积分
21
为什么有这篇小作文?

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

项目构成


  • 后端部分:使用golang的gin框架起的服务
  • 前端部分:vue+elementui
先说后端部分,后端逻辑相对前端简单点,关键三步
登陆接口生成双token
  1. "github.com/dgrijalva/jwt-go"
复制代码
  1. func (this UserController) DoLogin(ctx *gin.Context) {
  2.     username := ctx.Request.FormValue("username")
  3.     passWord := ctx.Request.FormValue("password")
  4.     passMd5 := middlewares.CreateMD5(passWord)
  5.     expireTime := time.Now().Add(10 * time.Second).Unix() //token过期时间10秒,主要是测试方便
  6.     refreshTime := time.Now().Add(20 * time.Second).Unix() //刷新的时间限制,超过20秒重新登录
  7.     user := []modules.User{}
  8.     err := modules.DB.Model(&modules.User{}).Where("username = ? AND password = ?", username, passMd5).Find(&user).Error
  9.     if err != nil || len(user) == 0 {
  10.         ctx.JSON(400, gin.H{
  11.             "success": false,
  12.             "message": "用户名或密码错误",
  13.         })
  14.     } else {
  15.         println("expireTime", string(rune(expireTime)))
  16.         myClaims := MyClaims{
  17.             user.Id,
  18.             jwt.StandardClaims{
  19.                 ExpiresAt: expireTime,
  20.             },
  21.         }
  22.         myClaimsRefrrsh := MyClaims{
  23.             user.Id,
  24.             jwt.StandardClaims{
  25.                 ExpiresAt: refreshTime,
  26.             },
  27.         }
  28.         jwtKey := []byte("lyf123456")
  29.         tokenObj := jwt.NewWithClaims(jwt.SigningMethodHS256, myClaims)
  30.         tokenStr, err := tokenObj.SignedString(jwtKey)
  31.         tokenFresh := jwt.NewWithClaims(jwt.SigningMethodHS256, myClaimsRefrrsh)
  32.         tokenStrRefresh, err2 := tokenFresh.SignedString(jwtKey)
  33.         if err != nil && err2 != nil {
  34.             ctx.JSON(200, gin.H{
  35.                 "message": "生成token失败",
  36.                 "success": false,
  37.             })
  38.         } else {
  39.             ctx.JSON(200, gin.H{
  40.                 "message":      "登录成功",
  41.                 "success":      true,
  42.                 "token":        tokenStr,//数据请求的token
  43.                 "refreshToken": tokenStrRefresh,//刷新token用的
  44.             })
  45.         }
  46.     }
  47. }
复制代码
刷新token的方法
  1. func (this UserController) RefrshToken(ctx *gin.Context) {
  2.     tokenData := ctx.Request.Header.Get("Authorization") //这里是个关键点,刷新token时也要带上token,不过这里是前端传的refreshToken
  3.     if tokenData == "" {
  4.         ctx.JSON(401, gin.H{
  5.             "message": "token为空",
  6.             "success": false,
  7.         })
  8.         ctx.Abort()
  9.         return
  10.     }
  11.     tokenStr := strings.Split(tokenData, " ")[1]
  12.     _, claims, err := middlewares.ParseToken(tokenStr)
  13.     expireTime := time.Now().Add(10 * time.Second).Unix()
  14.     refreshTime := time.Now().Add(20 * time.Second).Unix()
  15.     if err != nil {
  16.         ctx.JSON(400, gin.H{
  17.             "success": false,
  18.             "message": "token传入错误",
  19.         })
  20.     } else {
  21.         myClaims := MyClaims{
  22.             claims.Uid,
  23.             jwt.StandardClaims{
  24.                 ExpiresAt: expireTime,
  25.             },
  26.         }
  27.         myClaimsRefrrsh := MyClaims{
  28.             claims.Uid,
  29.             jwt.StandardClaims{
  30.                 ExpiresAt: refreshTime,
  31.             },
  32.         }
  33.         jwtKey := []byte("lyf123456")
  34.         tokenObj := jwt.NewWithClaims(jwt.SigningMethodHS256, myClaims)
  35.         tokenStr, err := tokenObj.SignedString(jwtKey)
  36.         tokenFresh := jwt.NewWithClaims(jwt.SigningMethodHS256, myClaimsRefrrsh)
  37.         tokenStrRefresh, err2 := tokenFresh.SignedString(jwtKey)
  38.         if err != nil && err2 != nil {
  39.             ctx.JSON(400, gin.H{
  40.                 "message": "生成token失败",
  41.                 "success": false,
  42.             })
  43.         } else {
  44.             ctx.JSON(200, gin.H{
  45.                 "message":      "刷新token成功",
  46.                 "success":      true,
  47.                 "token":        tokenStr,
  48.                 "refreshToken": tokenStrRefresh,
  49.             })
  50.         }
  51.     }
  52. }
复制代码
路由中间件里验证token
  1. package middlewares

  2. import (
  3.     "strings"

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

  7. type MyClaims struct {
  8.     Uid int
  9.     jwt.StandardClaims
  10. }

  11. func AuthMiddleWare(c *gin.Context) {
  12.     tokenData := c.Request.Header.Get("Authorization")
  13.     if tokenData == "" {
  14.         c.JSON(401, gin.H{
  15.             "message": "token为空",
  16.             "success": false,
  17.         })
  18.         c.Abort()
  19.         return
  20.     }
  21.     tokenStr := strings.Split(tokenData, " ")[1]
  22.     token, _, err := ParseToken(tokenStr)
  23.     if err != nil || !token.Valid {
  24.          // 这里我感觉觉是个关键点,我看别人写的,过期了返回401,但是前端的axios的响应拦截器里捕获不到,所以我用201状态码,
  25.         c.JSON(201, gin.H{
  26.             "message": "token已过期",
  27.             "success": false,
  28.         })
  29.         c.Abort()
  30.         return
  31.     } else {
  32.         c.Next()
  33.     }
  34. }

  35. func ParseToken(tokenStr string) (*jwt.Token, *MyClaims, error) {
  36.     jwtKey := []byte("lyf123456")
  37.     // 解析token
  38.     myClaims := &MyClaims{}
  39.     token, err := jwt.ParseWithClaims(tokenStr, myClaims, func(token *jwt.Token) (interface{}, error) {
  40.         return jwtKey, nil
  41.     })
  42.     return token, myClaims, err
  43. }
复制代码
总结一下:后端部分三步,1.登陆时生成双token,2,路由中间件里验证token,过期时返回201状态码(201是我私人定的,并不是行业标准)。3,刷新token的方法里也和登陆接口一样返回双token

前端部分

前端部分在axios封装时候加拦截器判断token是否过期,我这里跟别人写的最大的不同点是:我创建了两个axios对象,一个正常数据请求用(server),另一个专门刷新token用(serverRefreshToken),这样写的好处是省去了易错的判断逻辑
  1. import axios from 'axios'
  2. import { ElMessage } from 'element-plus'
  3. import router from '../router'
  4. //数据请求用
  5. const server=axios.create({
  6.   baseURL:'/shopApi',
  7.   timeout:5000
  8. })
  9. // 刷新token专用
  10. const serverRefreshToken=axios.create({
  11.   baseURL:'/shopApi',
  12.   timeout:5000
  13. })
  14. //获取新token的方法
  15. async function getNewToken(){
  16.   let res=await serverRefreshToken.request({
  17.     url:`/admin/refresh`,
  18.     method:"post",
  19.   })
  20.   if(res.status==200){
  21.     sessionStorage.setItem("token",res.data.token)
  22.     sessionStorage.setItem("refreshToken",res.data.refreshToken)
  23.     return true
  24.   }else{
  25.     ElMessage.error(res.data.message)
  26.     router.push('/login')
  27.     return false
  28.   }
  29. }
  30. //这里是正常获取数据用的请求拦截器,主要作用是给所有请求的请求头里加上token
  31. server.interceptors.request.use(config=>{
  32.   let token=""
  33.   token=sessionStorage.getItem("token")
  34.   if(token){
  35.     config.headers.Authorization="Bearer "+token
  36.   }
  37.   return config
  38. },error=>{
  39.   Promise.reject(error)
  40. })
  41. //这里是正常获取数据用的响应拦截器,正常数据请求都是200状态码,当拦截到201状态码时,代表token过期了,
  42. // 应热心小伙伴的提醒,加上防止token过期后正好短时间内多个请求重复刷新token,刷新token成功再请求
  43. let isRefreshing=false
  44. let refreshFnArr=[]
  45. server.interceptors.response.use(async(res)=>{
  46.   if(res.status==201){
  47.     if(!isRefreshing){
  48.     // 如果正好段时间内触发了多个请求
  49.       isRefreshing=true
  50.       let bl=await getNewToken()
  51.       if(bl){
  52.         refreshFnArr.forEach(fn=>{
  53.           fn()
  54.         })
  55.         refreshFnArr=[]
  56.         res= await server.request(res.config)
  57.         isRefreshing=false
  58.       }
  59.     }else{
  60.       return new Promise(resolve=>{
  61.         refreshFnArr.push(
  62.           ()=>{
  63.             resolve(res.config)
  64.           }
  65.         )
  66.       })
  67.     }
  68.   }
  69.   return res
  70. },error=>{
  71.   if(error.response.status==500||error.response.status==401||error.response.status==400){
  72.     router.push('/login')
  73.     ElMessage.error(error.response.data.message)
  74.     Promise.reject(error)
  75.   }
  76.   
  77. })
  78. //这里是刷新token专用的axios对象,他的作用是给请求加上刷新token专用的refreshToken
  79. serverRefreshToken.interceptors.request.use(config=>{
  80.   let token=""
  81.   token=sessionStorage.getItem("refreshToken")
  82.   if(token){
  83.     config.headers.Authorization="Bearer "+token
  84.   }
  85.   return config
  86. },error=>{
  87.   Promise.reject(error)
  88. })
  89. export default server
复制代码
总结一下,前端部分:1,正常数据请求和刷新token用的请求分开了,各司其职。省去复杂的判断。2,获取新的token和refreshToken后更新原来旧的token和refreshToken。(完结)
到此这篇关于vue中双token和无感刷新token的区别的文章就介绍到这了,更多相关vue中双token和无感刷新token内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

来源:https://www.jb51.net/javascript/32097616i.htm
免责声明:由于采集信息均来自互联网,如果侵犯了您的权益,请联系我们【E-Mail:cb@itdo.tech】 我们会及时删除侵权内容,谢谢合作!

举报 回复 使用道具