vue中雙token和無感刷新token的區(qū)別
為什么有這篇小作文?
最近要給自己的項目加上token自動續(xù)期,但是在網(wǎng)上搜的寫法五花八門,有的光前端部分就寫了幾百行代碼,我看著費勁,摸了半天也沒有實現(xiàn),所以決定自己造輪子
項目構(gòu)成
- 后端部分:使用golang的gin框架起的服務(wù)
- 前端部分:vue+elementui
先說后端部分,后端邏輯相對前端簡單點,關(guān)鍵三步
登陸接口生成雙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,//數(shù)據(jù)請求的token "refreshToken": tokenStrRefresh,//刷新token用的 }) } } }
刷新token的方法
func (this UserController) RefrshToken(ctx *gin.Context) { tokenData := ctx.Request.Header.Get("Authorization") //這里是個關(guān)鍵點,刷新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 { // 這里我感覺覺是個關(guān)鍵點,我看別人寫的,過期了返回401,但是前端的axios的響應(yīng)攔截器里捕獲不到,所以我用201狀態(tài)碼, 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 }
總結(jié)一下:后端部分三步,1.登陸時生成雙token,2,路由中間件里驗證token,過期時返回201狀態(tài)碼(201是我私人定的,并不是行業(yè)標(biāo)準(zhǔn))。3,刷新token的方法里也和登陸接口一樣返回雙token
前端部分
前端部分在axios封裝時候加攔截器判斷token是否過期,我這里跟別人寫的最大的不同點是:我創(chuàng)建了兩個axios對象,一個正常數(shù)據(jù)請求用(server),另一個專門刷新token用(serverRefreshToken),這樣寫的好處是省去了易錯的判斷邏輯
import axios from 'axios' import { ElMessage } from 'element-plus' import router from '../router' //數(shù)據(jù)請求用 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 } } //這里是正常獲取數(shù)據(jù)用的請求攔截器,主要作用是給所有請求的請求頭里加上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) }) //這里是正常獲取數(shù)據(jù)用的響應(yīng)攔截器,正常數(shù)據(jù)請求都是200狀態(tài)碼,當(dāng)攔截到201狀態(tài)碼時,代表token過期了, // 應(yīng)熱心小伙伴的提醒,加上防止token過期后正好短時間內(nèi)多個請求重復(fù)刷新token,刷新token成功再請求 let isRefreshing=false let refreshFnArr=[] server.interceptors.response.use(async(res)=>{ if(res.status==201){ if(!isRefreshing){ // 如果正好段時間內(nèi)觸發(fā)了多個請求 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
總結(jié)一下,前端部分:1,正常數(shù)據(jù)請求和刷新token用的請求分開了,各司其職。省去復(fù)雜的判斷。2,獲取新的token和refreshToken后更新原來舊的token和refreshToken。(完結(jié))
到此這篇關(guān)于vue中雙token和無感刷新token的區(qū)別的文章就介紹到這了,更多相關(guān)vue中雙token和無感刷新token內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
微信小程序登錄數(shù)據(jù)解密及狀態(tài)維持實例詳解
這篇文章主要介紹了微信小程序登錄數(shù)據(jù)解密及狀態(tài)維持,結(jié)合實例形式分析了微信小程序解密敏感信息及獲取session保持登陸狀態(tài)的相關(guān)操作技巧,需要的朋友可以參考下2019-05-05Draggable Elements 元素拖拽功能實現(xiàn)代碼
雖說js框架到處都是, 都封裝了很多實用的功能,能快速的讓我們實現(xiàn)如動畫,元素拖拽等功能, 不過由于好奇心的驅(qū)使, 有時想一探究竟, 看看一些功能是如何實現(xiàn)的2011-03-03簡單聊聊JavaScript的事件循環(huán)機(jī)制
前端開發(fā)的童鞋應(yīng)該都知道,JavaScript是一門單線程的腳本語言,這就意味著JavaScript 代碼在執(zhí)行的時候,只有一個主線程來執(zhí)行所有的任務(wù),同一個時間只能做同一件事情,這篇文章主要給大家介紹了關(guān)于JavaScript事件循環(huán)機(jī)制的相關(guān)資料,需要的朋友可以參考下2022-03-03js getBoundingClientRect() 來獲取頁面元素的位置
該方法已經(jīng)不再是IE Only了,F(xiàn)F3.0+和Opera9.5+已經(jīng)支持了該方法,可以說在獲得頁面元素位置上效率能有很大的提高,在以前版本的Opera和Firefox中必須通過循環(huán)來獲得元素在頁面中的絕對位置。2010-11-11JavaScript變量聲明的var、let、const詳解
JavaScript中的變量是松散類型的,可以保存任何類型數(shù)據(jù),變量只不過是一個名稱,下面這篇文章主要給大家介紹了關(guān)于JavaScript變量聲明的var、let、const的相關(guān)資料,需要的朋友可以參考下2022-07-07