Go JWT身份验证中间件
#go #jwt #backend

我是Amit Shekhar,一位导师,帮助开发人员获得高薪技术工作。

在此博客中,我们将在GO(Golang)中学习JWT身份验证中间件的实现。

首先,我必须提到OAuth 2.0协议中使用JWT(JSON Web令牌)的使用是对用户进行战斗测试的一种方式。

虽然大多数基于令牌的身份验证实现都需要数据库查询来验证授予的令牌的有效性,但JWT不需要数据库查询即可验证。此外,使用JWT,我们可以编码一些数据(例如用户ID)作为有效载荷,并且可以在需要时解码以获取数据。

我们所有人都在使用Java,JavaScript,Python等不同语言的项目中使用了它。

本文最初发表在amitshekhar.me

让我们去:)

我将使用以下项目用于实施部分。该项目以GO语言进行了干净的体系结构。您可以找到项目本身博客中提到的JWT身份验证中间件的完整代码。

链接到项目:Go Backend Clean Architecture

我们将使用以下软件包在GO中使用JWT:

go get -u github.com/golang-jwt/jwt/v4

然后,将其导入您的代码:

import "github.com/golang-jwt/jwt/v4"

首先,我们必须在域软件包中创建一个文件jwt_custom.go

package domain

import (
    "github.com/golang-jwt/jwt/v4"
)

type JwtCustomClaims struct {
    Name string `json:"name"`
    ID   string `json:"id"`
    jwt.StandardClaims
}

type JwtCustomRefreshClaims struct {
    ID string `json:"id"`
    jwt.StandardClaims
}

和假设,我们有一个struct User如下:

package domain

type User struct {
    ID       string
    Name     string
}

我们还必须在我们的配置文件(例如.envconfig.json)中为访问令牌和刷新令牌放置秘密密钥和到期时间。

ACCESS_TOKEN_EXPIRY_HOUR = 2
REFRESH_TOKEN_EXPIRY_HOUR = 168
ACCESS_TOKEN_SECRET=access_token_secret
REFRESH_TOKEN_SECRET=refresh_token_secret

我已经为此使用了.env文件,然后将其加载到struct Env中。

type Env struct {
    AccessTokenExpiryHour  int
    RefreshTokenExpiryHour int
    AccessTokenSecret      string
    RefreshTokenSecret     string
}

然后,我们必须创建一个文件tokenutil.go。我已经在内部软件包下的Tokenutil软件包内创建了这个。

在此文件中,我们有以下功能:

  • createAccessToken(用户 *domain.user,秘密字符串,有效int)
  • createrefreshtoken(用户 *域。
  • ISAUSATORIAD(requestToken String,Secret String)
  • dectractIdfromtoken(requestToken String,Secret String)

让我们了解这些功能一个一个一个。

createAccessToken(用户 *domain.user,秘密字符串,有效int)

此功能采用三个参数:用户,访问秘密和有效期。

它通过编码由用户NameID组成的有效载荷创建一个令牌

createrefreshtoken(用户 *域。

此功能还采用三个参数:用户,刷新秘密和到期。

,它通过编码由用户ID组成的有效载荷创建一个令牌

ISAUSATORIAD(requestToken String,Secret String)

此功能执行检查给定令牌是否授权的任务。就是这样。

dectractIdfromtoken(requestToken String,Secret String)

此函数解码并提取创建令牌时编码的ID

tokenutil.go文件中存在完整的代码。

package tokenutil

import (
    "fmt"
    "time"

    "github.com/amitshekhariitbhu/go-backend-clean-architecture/domain"
    jwt "github.com/golang-jwt/jwt/v4"
)

func CreateAccessToken(user *domain.User, secret string, expiry int) (accessToken string, err error) {
    exp := time.Now().Add(time.Hour * time.Duration(expiry)).Unix()
    claims := &domain.JwtCustomClaims{
        Name: user.Name,
        ID:   user.ID,
        StandardClaims: jwt.StandardClaims{
            ExpiresAt: exp,
        },
    }
    token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
    t, err := token.SignedString([]byte(secret))
    if err != nil {
        return "", err
    }
    return t, err
}

func CreateRefreshToken(user *domain.User, secret string, expiry int) (refreshToken string, err error) {
    claimsRefresh := &domain.JwtCustomRefreshClaims{
        ID: user.ID,
        StandardClaims: jwt.StandardClaims{
            ExpiresAt: time.Now().Add(time.Hour * time.Duration(expiry)).Unix(),
        },
    }
    token := jwt.NewWithClaims(jwt.SigningMethodHS256, claimsRefresh)
    rt, err := token.SignedString([]byte(secret))
    if err != nil {
        return "", err
    }
    return rt, err
}

func IsAuthorized(requestToken string, secret string) (bool, error) {
    _, err := jwt.Parse(requestToken, 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(secret), nil
    })
    if err != nil {
        return false, err
    }
    return true, nil
}

func ExtractIDFromToken(requestToken string, secret string) (string, error) {
    token, err := jwt.Parse(requestToken, 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(secret), nil
    })

    if err != nil {
        return "", err
    }

    claims, ok := token.Claims.(jwt.MapClaims)

    if !ok && !token.Valid {
        return "", fmt.Errorf("Invalid Token")
    }

    return claims["id"].(string), nil
}

接下来,我们将创建中间件包。在此,我们将创建jwt_auth_middleware.go文件。

在这里,我们需要理解,基于我们使用的HTTP Web框架,我们将将处理程序函数与该框架相对应。在我们的情况下,我们正在使用Gin HTTP Web框架。因此,我们将使用gin.HandlerFunc

但是,除了与该框架相关的修改外,我们选择的任何框架中的逻辑都将相同。

该文件中的代码如下:

package middleware

import (
    "net/http"
    "strings"

    "github.com/amitshekhariitbhu/go-backend-clean-architecture/domain"
    "github.com/amitshekhariitbhu/go-backend-clean-architecture/internal/tokenutil"
    "github.com/gin-gonic/gin"
)

func JwtAuthMiddleware(secret string) gin.HandlerFunc {
    return func(c *gin.Context) {
        authHeader := c.Request.Header.Get("Authorization")
        t := strings.Split(authHeader, " ")
        if len(t) == 2 {
            authToken := t[1]
            authorized, err := tokenutil.IsAuthorized(authToken, secret)
            if authorized {
                userID, err := tokenutil.ExtractIDFromToken(authToken, secret)
                if err != nil {
                    c.JSON(http.StatusUnauthorized, domain.ErrorResponse{Message: err.Error()})
                    c.Abort()
                    return
                }
                c.Set("x-user-id", userID)
                c.Next()
                return
            }
            c.JSON(http.StatusUnauthorized, domain.ErrorResponse{Message: err.Error()})
            c.Abort()
            return
        }
        c.JSON(http.StatusUnauthorized, domain.ErrorResponse{Message: "Not authorized"})
        c.Abort()
    }
}

它将访问秘密密钥作为输入参数。

首先,它从请求的标题中提取令牌。

然后,它检查令牌是否已授权,如果未授权,它会返回错误。

如果已授权,它将从我们在创建访问令牌时所投入的令牌中提取UserID。然后将其放入使用的HTTP Web框架的上下文中,以便我们可以在请求流中以后在需要时轻松提取它。

我们可以从HTTP Web框架上下文中获取UserID

userID := c.GetString("x-user-id")

然后,我们可以使用以下的此中间件:

router.Use(middleware.JwtAuthMiddleware(env.AccessTokenSecret))

当我们要生成访问令牌时,我们可以致电:

accessToken, err := tokenutil.CreateAccessToken(user, secret, expiry)

为了生成刷新令牌,我们可以致电:

refreshToken, err := tokenutil.CreateRefreshToken(user, secret, expiry)

这是我们可以在GO中使用JWT身份验证中间件的方法。

我强烈建议您通过项目并检查JWT身份验证中间件实施的端到端。

链接到项目:Go Backend Clean Architecture

现在就是这样。

谢谢

Amit Shekhar

您可以与我联系:

Read all of my high-quality blogs here.