欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Go語(yǔ)言中雙Token登錄系統(tǒng)的思路與實(shí)現(xiàn)詳解

 更新時(shí)間:2025年07月09日 09:16:25   作者:Go Dgg  
在現(xiàn)代Web應(yīng)用中,身份認(rèn)證是保障系統(tǒng)安全的重要環(huán)節(jié),本文將介紹如何使用Go語(yǔ)言實(shí)現(xiàn)雙Token登錄系統(tǒng),文中的示例代碼講解詳細(xì),需要的可以了解下

引言

在現(xiàn)代Web應(yīng)用中,身份認(rèn)證是保障系統(tǒng)安全的重要環(huán)節(jié)。傳統(tǒng)的單Token認(rèn)證方式存在一些安全隱患,如Token泄露可能導(dǎo)致長(zhǎng)期風(fēng)險(xiǎn)。雙Token機(jī)制(Access Token + Refresh Token)提供了更好的安全性和用戶體驗(yàn)。本文將介紹如何使用Go語(yǔ)言實(shí)現(xiàn)雙Token登錄系統(tǒng)。

雙Token機(jī)制概述

雙Token機(jī)制包含兩種令牌:

  • Access Token:短期有效的令牌,用于訪問(wèn)受保護(hù)資源
  • Refresh Token:長(zhǎng)期有效的令牌,用于獲取新的Access Token

這種機(jī)制的優(yōu)勢(shì)在于:

  • Access Token有效期短,即使泄露影響有限
  • Refresh Token不直接用于資源訪問(wèn),降低了泄露風(fēng)險(xiǎn)
  • 無(wú)需頻繁重新登錄,保持用戶體驗(yàn)

實(shí)現(xiàn)思路

1. 數(shù)據(jù)結(jié)構(gòu)設(shè)計(jì)

首先定義Token相關(guān)的數(shù)據(jù)結(jié)構(gòu):

type TokenDetails struct {
    AccessToken  string
    RefreshToken string
    AccessUuid   string
    RefreshUuid  string
    AtExpires    int64
    RtExpires    int64
}

type AccessDetails struct {
    AccessUuid string
    UserId     uint64
}

2. Token生成與存儲(chǔ)

使用JWT(JSON Web Token)生成Token,并存儲(chǔ)在Redis中:

func CreateToken(userid uint64) (*TokenDetails, error) {
    td := &TokenDetails{}
    td.AtExpires = time.Now().Add(time.Minute * 15).Unix()
    td.AccessUuid = uuid.New().String()
    
    td.RtExpires = time.Now().Add(time.Hour * 24 * 7).Unix()
    td.RefreshUuid = uuid.New().String()
    
    // 創(chuàng)建Access Token
    atClaims := jwt.MapClaims{}
    atClaims["authorized"] = true
    atClaims["access_uuid"] = td.AccessUuid
    atClaims["user_id"] = userid
    atClaims["exp"] = td.AtExpires
    at := jwt.NewWithClaims(jwt.SigningMethodHS256, atClaims)
    td.AccessToken, _ = at.SignedString([]byte(os.Getenv("ACCESS_SECRET")))
    
    // 創(chuàng)建Refresh Token
    rtClaims := jwt.MapClaims{}
    rtClaims["refresh_uuid"] = td.RefreshUuid
    rtClaims["user_id"] = userid
    rtClaims["exp"] = td.RtExpires
    rt := jwt.NewWithClaims(jwt.SigningMethodHS256, rtClaims)
    td.RefreshToken, _ = rt.SignedString([]byte(os.Getenv("REFRESH_SECRET")))
    
    return td, nil
}

func CreateAuth(userid uint64, td *TokenDetails) error {
    at := time.Unix(td.AtExpires, 0)
    rt := time.Unix(td.RtExpires, 0)
    now := time.Now()
    
    // 存儲(chǔ)Access Token
    errAccess := client.Set(td.AccessUuid, strconv.Itoa(int(userid)), at.Sub(now)).Err()
    if errAccess != nil {
        return errAccess
    }
    
    // 存儲(chǔ)Refresh Token
    errRefresh := client.Set(td.RefreshUuid, strconv.Itoa(int(userid)), rt.Sub(now)).Err()
    if errRefresh != nil {
        return errRefresh
    }
    
    return nil
}

3. 登錄接口實(shí)現(xiàn)

func Login(c *gin.Context) {
    var user User
    if err := c.ShouldBindJSON(&user); err != nil {
        c.JSON(http.StatusUnprocessableEntity, "Invalid json provided")
        return
    }
    
    // 驗(yàn)證用戶憑據(jù)
    // ...
    
    // 生成Token
    td, err := CreateToken(user.ID)
    if err != nil {
        c.JSON(http.StatusUnprocessableEntity, err.Error())
        return
    }
    
    // 存儲(chǔ)Token
    saveErr := CreateAuth(user.ID, td)
    if saveErr != nil {
        c.JSON(http.StatusUnprocessableEntity, saveErr.Error())
        return
    }
    
    tokens := map[string]string{
        "access_token":  td.AccessToken,
        "refresh_token": td.RefreshToken,
    }
    
    c.JSON(http.StatusOK, tokens)
}

4. Token刷新機(jī)制

func Refresh(c *gin.Context) {
    mapToken := map[string]string{}
    if err := c.ShouldBindJSON(&mapToken); err != nil {
        c.JSON(http.StatusUnprocessableEntity, err.Error())
        return
    }
    refreshToken := mapToken["refresh_token"]
    
    // 驗(yàn)證Refresh Token
    token, err := jwt.Parse(refreshToken, func(token *jwt.Token) (interface{}, error) {
        if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
            return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"])
        }
        return []byte(os.Getenv("REFRESH_SECRET")), nil
    })
    
    if err != nil {
        c.JSON(http.StatusUnauthorized, "Refresh token expired")
        return
    }
    
    // 檢查Token是否有效
    if _, ok := token.Claims.(jwt.Claims); !ok && !token.Valid {
        c.JSON(http.StatusUnauthorized, err)
        return
    }
    
    // 提取claims
    claims, ok := token.Claims.(jwt.MapClaims)
    if ok && token.Valid {
        refreshUuid, ok := claims["refresh_uuid"].(string)
        if !ok {
            c.JSON(http.StatusUnprocessableEntity, err)
            return
        }
        
        userId, err := strconv.ParseUint(fmt.Sprintf("%.f", claims["user_id"]), 10, 64)
        if err != nil {
            c.JSON(http.StatusUnprocessableEntity, "Error occurred")
            return
        }
        
        // 刪除舊的Refresh Token
        deleted, delErr := DeleteAuth(refreshUuid)
        if delErr != nil || deleted == 0 {
            c.JSON(http.StatusUnauthorized, "unauthorized")
            return
        }
        
        // 創(chuàng)建新的Token對(duì)
        ts, createErr := CreateToken(userId)
        if createErr != nil {
            c.JSON(http.StatusForbidden, createErr.Error())
            return
        }
        
        // 保存新的Token
        saveErr := CreateAuth(userId, ts)
        if saveErr != nil {
            c.JSON(http.StatusForbidden, saveErr.Error())
            return
        }
        
        tokens := map[string]string{
            "access_token":  ts.AccessToken,
            "refresh_token": ts.RefreshToken,
        }
        
        c.JSON(http.StatusCreated, tokens)
    } else {
        c.JSON(http.StatusUnauthorized, "refresh expired")
    }
}

5. 中間件實(shí)現(xiàn)Token驗(yàn)證

func TokenAuthMiddleware() gin.HandlerFunc {
    return func(c *gin.Context) {
        err := TokenValid(c.Request)
        if err != nil {
            c.JSON(http.StatusUnauthorized, err.Error())
            c.Abort()
            return
        }
        c.Next()
    }
}

func TokenValid(r *http.Request) error {
    token, err := VerifyToken(r)
    if err != nil {
        return err
    }
    
    if _, ok := token.Claims.(jwt.Claims); !ok && !token.Valid {
        return err
    }
    
    return nil
}

func VerifyToken(r *http.Request) (*jwt.Token, error) {
    tokenString := ExtractToken(r)
    token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
        if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
            return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"])
        }
        return []byte(os.Getenv("ACCESS_SECRET")), nil
    })
    
    if err != nil {
        return nil, err
    }
    
    return token, nil
}

func ExtractToken(r *http.Request) string {
    bearToken := r.Header.Get("Authorization")
    strArr := strings.Split(bearToken, " ")
    if len(strArr) == 2 {
        return strArr[1]
    }
    return ""
}

完整流程

  • 用戶登錄:提供用戶名密碼,服務(wù)端驗(yàn)證后返回Access Token和Refresh Token
  • 訪問(wèn)受保護(hù)資源:客戶端在請(qǐng)求頭中攜帶Access Token
  • Access Token過(guò)期:服務(wù)端返回401錯(cuò)誤
  • 刷新Token:客戶端使用Refresh Token請(qǐng)求新的Token對(duì)
  • 繼續(xù)訪問(wèn):使用新的Access Token訪問(wèn)資源

總結(jié)

通過(guò)Go語(yǔ)言實(shí)現(xiàn)雙Token認(rèn)證機(jī)制,我們能夠構(gòu)建更安全的身份認(rèn)證系統(tǒng)。這種機(jī)制在保證安全性的同時(shí),也提供了良好的用戶體驗(yàn)。實(shí)際應(yīng)用中,可以根據(jù)業(yè)務(wù)需求調(diào)整Token的有效期和實(shí)現(xiàn)細(xì)節(jié)。

以上就是Go語(yǔ)言中雙Token登錄系統(tǒng)的思路與實(shí)現(xiàn)詳解的詳細(xì)內(nèi)容,更多關(guān)于Go雙Token登錄的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • go值賦值和引用賦值的使用

    go值賦值和引用賦值的使用

    本文將介紹Go語(yǔ)言中的值賦值和引用賦值,并比較它們之間的差異,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2023-10-10
  • 淺析Go中原子操作的重要性與使用

    淺析Go中原子操作的重要性與使用

    這篇文章主要帶大家一起探索?Go?中原子操作的概念,了解為什么它們是重要的,以及如何有效地使用它們,文中的示例代碼講解詳細(xì),需要的可以了解下
    2023-11-11
  • GO語(yǔ)言基礎(chǔ)庫(kù)os包的函數(shù)全面解析

    GO語(yǔ)言基礎(chǔ)庫(kù)os包的函數(shù)全面解析

    這篇文章主要為大家介紹了GO語(yǔ)言基礎(chǔ)庫(kù)os包的函數(shù)全面解析, 有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-12-12
  • golang實(shí)現(xiàn)枚舉的幾種方式

    golang實(shí)現(xiàn)枚舉的幾種方式

    在Go語(yǔ)言中,雖沒(méi)有內(nèi)置枚舉類型,但可通過(guò)常量、結(jié)構(gòu)體或自定義類型和方法實(shí)現(xiàn)枚舉功能,這些方法提高了代碼的可讀性和維護(hù)性,避免了魔法數(shù)字的使用,感興趣的可以了解一下
    2024-09-09
  • Go庫(kù)text與template包使用示例詳解

    Go庫(kù)text與template包使用示例詳解

    這篇文章主要為大家介紹了Go庫(kù)text與template包使用示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-12-12
  • Go?CSV包實(shí)現(xiàn)結(jié)構(gòu)體和csv內(nèi)容互轉(zhuǎn)工具詳解

    Go?CSV包實(shí)現(xiàn)結(jié)構(gòu)體和csv內(nèi)容互轉(zhuǎn)工具詳解

    這篇文章主要介紹了Go?CSV包實(shí)現(xiàn)結(jié)構(gòu)體和csv內(nèi)容互轉(zhuǎn)工具詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-03-03
  • Golang中使用JSON的一些小技巧分享

    Golang中使用JSON的一些小技巧分享

    這篇文章主要分享了Golang中使用JSON的一些小技巧,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起看看吧。
    2017-06-06
  • go包管理的具體實(shí)現(xiàn)

    go包管理的具體實(shí)現(xiàn)

    本文主要介紹了go包管理的具體實(shí)現(xiàn),包括包定義、自定義包路徑、init函數(shù)作用、導(dǎo)入方式及Go Modules配置,感興趣的可以了解一下
    2025-06-06
  • Golang文件操作之讀取與寫入方法全攻略

    Golang文件操作之讀取與寫入方法全攻略

    本文詳細(xì)介紹了在Go語(yǔ)言中進(jìn)行文件操作的方法,包括文件的創(chuàng)建、打開(kāi)、讀取、寫入和關(guān)閉等,解析了使用os、bufio和io包進(jìn)行高效文件操作的技巧,并提供了錯(cuò)誤處理與性能優(yōu)化的建議,以幫助開(kāi)發(fā)者有效管理文件資源并提升應(yīng)用性能,需要的朋友可以參考下
    2024-11-11
  • 詳解Go程序添加遠(yuǎn)程調(diào)用tcpdump功能

    詳解Go程序添加遠(yuǎn)程調(diào)用tcpdump功能

    這篇文章主要介紹了go程序添加遠(yuǎn)程調(diào)用tcpdump功能,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2022-05-05

最新評(píng)論