最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

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

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

引言

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

雙Token機制概述

雙Token機制包含兩種令牌:

  • Access Token:短期有效的令牌,用于訪問受保護資源
  • Refresh Token:長期有效的令牌,用于獲取新的Access Token

這種機制的優(yōu)勢在于:

  • Access Token有效期短,即使泄露影響有限
  • Refresh Token不直接用于資源訪問,降低了泄露風險
  • 無需頻繁重新登錄,保持用戶體驗

實現(xiàn)思路

1. 數(shù)據(jù)結構設計

首先定義Token相關的數(shù)據(jù)結構:

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

type AccessDetails struct {
    AccessUuid string
    UserId     uint64
}

2. Token生成與存儲

使用JWT(JSON Web Token)生成Token,并存儲在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()
    
    // 存儲Access Token
    errAccess := client.Set(td.AccessUuid, strconv.Itoa(int(userid)), at.Sub(now)).Err()
    if errAccess != nil {
        return errAccess
    }
    
    // 存儲Refresh Token
    errRefresh := client.Set(td.RefreshUuid, strconv.Itoa(int(userid)), rt.Sub(now)).Err()
    if errRefresh != nil {
        return errRefresh
    }
    
    return nil
}

3. 登錄接口實現(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
    }
    
    // 驗證用戶憑據(jù)
    // ...
    
    // 生成Token
    td, err := CreateToken(user.ID)
    if err != nil {
        c.JSON(http.StatusUnprocessableEntity, err.Error())
        return
    }
    
    // 存儲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刷新機制

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"]
    
    // 驗證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對
        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. 中間件實現(xiàn)Token驗證

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 ""
}

完整流程

  • 用戶登錄:提供用戶名密碼,服務端驗證后返回Access Token和Refresh Token
  • 訪問受保護資源:客戶端在請求頭中攜帶Access Token
  • Access Token過期:服務端返回401錯誤
  • 刷新Token:客戶端使用Refresh Token請求新的Token對
  • 繼續(xù)訪問:使用新的Access Token訪問資源

總結

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

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

相關文章

  • go語言題解LeetCode1160拼寫單詞示例詳解

    go語言題解LeetCode1160拼寫單詞示例詳解

    這篇文章主要為大家介紹了go語言題解LeetCode1160拼寫單詞示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-12-12
  • Golang標準庫之errors包應用方式

    Golang標準庫之errors包應用方式

    Go語言的errors包提供了基礎的錯誤處理能力,允許通過errors.New創(chuàng)建自定義error對象,error在Go中是一個接口,通過實現(xiàn)Error方法來定義錯誤文本,對錯誤的比較通?;趯ο蟮刂?而非文本內容,因此即使兩個錯誤文本相同
    2024-10-10
  • golang泛型Generics的實現(xiàn)

    golang泛型Generics的實現(xiàn)

    本文主要介紹了golang泛型Generics的實現(xiàn),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2026-04-04
  • Go語言報錯:'godoc'?不是內部或外部命令,也不是可運行的程序(godoc無法使用處理)解決方法

    Go語言報錯:'godoc'?不是內部或外部命令,也不是可運行的程序(godoc無法使用處理)解決

    這篇文章主要介紹了Go語言報錯:'godoc'?不是內部或外部命令,也不是可運行的程序(godoc無法使用處理)解決方法,詳細描述了Go語言godoc命令無法使用的原因、解決方法與相關注意事項,需要的朋友可以參考下
    2024-01-01
  • 詳解Golang中Channel的原理和使用技巧

    詳解Golang中Channel的原理和使用技巧

    Channel管道提供了一種機制,它在兩個并發(fā)執(zhí)行的協(xié)程之間進行同步,并通過傳遞與該管道元素類型相符的值來進行通信。本文主要介紹了Channel的原理和使用技巧,需要的可以參考一下
    2022-11-11
  • Go標準庫日志打印及同時輸出到控制臺與文件

    Go標準庫日志打印及同時輸出到控制臺與文件

    Go語言內置的log包實現(xiàn)了簡單的日志服務,下面這篇文章主要給大家介紹了關于Go標準庫日志打印及同時輸出到控制臺與文件的相關資料,文中通過實例代碼介紹的非常詳細,需要的朋友可以參考下
    2022-11-11
  • beego獲取ajax數(shù)據(jù)的實例

    beego獲取ajax數(shù)據(jù)的實例

    下面小編就為大家分享一篇beego獲取ajax數(shù)據(jù)的實例,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2017-12-12
  • golang中GOPROXY如何設置

    golang中GOPROXY如何設置

    在Go語言開發(fā)中,合理設置 GOPROXY 環(huán)境變量可以顯著提升依賴包的下載速度和穩(wěn)定性,針對高級場景,可以配置多代理冗余、禁止代理訪問特定包或關閉校驗和驗證,下面就來詳細的介紹一下
    2026-02-02
  • 詳解Golang time包中的time.Duration類型

    詳解Golang time包中的time.Duration類型

    在日常開發(fā)過程中,會頻繁遇到對時間進行操作的場景,使用 Golang 中的 time 包可以很方便地實現(xiàn)對時間的相關操作,本文講解一下 time 包中的 time.Duration 類型,需要的朋友可以參考下
    2023-07-07
  • Go語言context上下文管理的使用

    Go語言context上下文管理的使用

    本文主要介紹了Go語言context上下文管理的使用,文中通過示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-03-03

最新評論

桐梓县| 天祝| 南部县| 焉耆| 兴业县| 陇西县| 凤凰县| 开化县| 阳原县| 滦南县| 皮山县| 沂南县| 全南县| 兴化市| 边坝县| 宿州市| 伊金霍洛旗| 嘉鱼县| 红桥区| 闽侯县| 东丽区| 手机| 新安县| 日喀则市| 茂名市| 治多县| 勃利县| 清苑县| 屏山县| 原阳县| 会理县| 密云县| 乡宁县| 玉树县| 伊宁县| 于都县| 台南县| 商洛市| 聂荣县| 泰兴市| 沭阳县|