golang,go,博客,开源,编程

golang实现jwt

Published on with 0 views and 0 comments

在 Go 语言中实现 JWT 认证非常简单,通常使用第三方库来简化签名、验证等操作。最常用的库是 github.com/dgrijalva/jwt-go(或其继任者 github.com/golang-jwt/jwt)。以下是如何在 Go 中实现一个完整的 JWT 认证流程的详细步骤。

安装依赖

首先,确保安装了必要的依赖:

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

1. 生成 JWT

生成 JWT 需要使用 jwt-go 库来创建一个新的令牌。在生成过程中,我们会创建一个包含用户信息的负载,并使用密钥对其进行签名。

示例代码:

package main

import (
    "fmt"
    "time"
    "log"

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

var secretKey = []byte("your-256-bit-secret")

// 定义自定义声明
type CustomClaims struct {
    Username string `json:"username"`
    Role     string `json:"role"`
    jwt.RegisteredClaims
}

func generateJWT(username, role string) (string, error) {
    // 设置负载数据
    claims := CustomClaims{
        Username: username,
        Role:     role,
        RegisteredClaims: jwt.RegisteredClaims{
            Issuer:    "myapp",
            ExpiresAt: jwt.NewNumericDate(time.Now().Add(24 * time.Hour)), // 设置过期时间为24小时
            IssuedAt:  jwt.NewNumericDate(time.Now()), // 设置当前时间
        },
    }

    // 创建一个新的token
    token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)

    // 使用secretKey对token进行签名
    signedToken, err := token.SignedString(secretKey)
    if err != nil {
        return "", err
    }

    return signedToken, nil
}

func main() {
    token, err := generateJWT("john_doe", "admin")
    if err != nil {
        log.Fatal(err)
    }

    fmt.Println("Generated JWT:", token)
}

解释:

  • CustomClaims: 这是自定义的声明结构体,包含了 UsernameRole,以及 JWT 的标准声明(RegisteredClaims)。
  • jwt.NewWithClaims:这个方法用来生成一个新的JWT,SigningMethodHS256 是使用的签名算法(HMAC-SHA256)。
  • SignedString(secretKey):使用我们定义的 secretKey 对生成的 JWT 进行签名。

2. 解析 JWT

为了验证 JWT 的合法性,我们需要解析 JWT 并验证其签名。此过程会验证令牌的有效性,包括签名、过期时间等。

示例代码:

package main

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

func parseJWT(tokenString string) (*CustomClaims, error) {
    // 解析token时,提供一个回调函数来验证token的签名
    token, err := jwt.ParseWithClaims(tokenString, &CustomClaims{}, func(token *jwt.Token) (interface{}, error) {
        // 这里我们验证token的签名算法
        if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
            return nil, fmt.Errorf("unexpected signing method %v", token.Header["alg"])
        }
        // 返回密钥
        return secretKey, nil
    })
  
    if err != nil {
        return nil, err
    }

    // 如果token合法,返回包含数据的claims
    if claims, ok := token.Claims.(*CustomClaims); ok && token.Valid {
        return claims, nil
    }

    return nil, fmt.Errorf("invalid token")
}

func main() {
    // 假设我们从前端接收到的JWT
    tokenString := "your-jwt-token-here"

    claims, err := parseJWT(tokenString)
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Token is valid!\nUsername: %s\nRole: %s\n", claims.Username, claims.Role)
}

解释:

  • jwt.ParseWithClaims: 这个方法会解析并验证 JWT,&CustomClaims{} 表示将 JWT 的负载映射到我们的自定义声明结构体 CustomClaims
  • 签名验证func(token *jwt.Token) 用来返回验证 JWT 的密钥。

3. 完整的认证流程

你可以在 Web 应用中使用 JWT 来实现用户认证流程。假设我们有一个登录端点来生成 JWT,并且需要保护一些需要认证的资源(如用户信息)。

示例代码:

package main

import (
    "fmt"
    "log"
    "net/http"
    "time"

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

var secretKey = []byte("your-256-bit-secret")

type CustomClaims struct {
    Username string `json:"username"`
    Role     string `json:"role"`
    jwt.RegisteredClaims
}

func generateJWT(username, role string) (string, error) {
    claims := CustomClaims{
        Username: username,
        Role:     role,
        RegisteredClaims: jwt.RegisteredClaims{
            Issuer:    "myapp",
            ExpiresAt: jwt.NewNumericDate(time.Now().Add(24 * time.Hour)),
            IssuedAt:  jwt.NewNumericDate(time.Now()),
        },
    }

    token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
    signedToken, err := token.SignedString(secretKey)
    if err != nil {
        return "", err
    }

    return signedToken, nil
}

func parseJWT(tokenString string) (*CustomClaims, error) {
    token, err := jwt.ParseWithClaims(tokenString, &CustomClaims{}, 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 secretKey, nil
    })

    if err != nil {
        return nil, err
    }

    if claims, ok := token.Claims.(*CustomClaims); ok && token.Valid {
        return claims, nil
    }

    return nil, fmt.Errorf("invalid token")
}

// 登录端点,返回JWT
func login(w http.ResponseWriter, r *http.Request) {
    username := r.URL.Query().Get("username")
    role := r.URL.Query().Get("role")

    token, err := generateJWT(username, role)
    if err != nil {
        http.Error(w, "Could not generate token", http.StatusInternalServerError)
        return
    }

    w.Header().Set("Content-Type", "application/json")
    fmt.Fprintf(w, `{"token": "%s"}`, token)
}

// 受保护资源端点
func protected(w http.ResponseWriter, r *http.Request) {
    tokenString := r.Header.Get("Authorization")
    if tokenString == "" {
        http.Error(w, "Authorization header missing", http.StatusUnauthorized)
        return
    }

    claims, err := parseJWT(tokenString)
    if err != nil {
        http.Error(w, "Invalid token", http.StatusUnauthorized)
        return
    }

    w.Header().Set("Content-Type", "application/json")
    fmt.Fprintf(w, `{"message": "Welcome %s!", "role": "%s"}`, claims.Username, claims.Role)
}

func main() {
    http.HandleFunc("/login", login)
    http.HandleFunc("/protected", protected)

    log.Fatal(http.ListenAndServe(":8080", nil))
}

解释:

  • /login:模拟一个用户登录过程,返回一个 JWT 令牌。
  • /protected:一个受保护的端点,只有提供有效 JWT 的用户才能访问。

使用示例:

  1. 访问 http://localhost:8080/login?username=john_doe&role=admin 生成 JWT。
  2. 使用获得的 JWT 作为 Authorization 头部,访问 http://localhost:8080/protected,可以看到受保护的资源内容。

总结

  • 在 Go 中实现 JWT 认证需要创建和解析 JWT,通常使用 github.com/golang-jwt/jwt 库。
  • 生成 JWT 时,你需要使用 jwt.NewWithClaims 来创建 JWT,并使用密钥进行签名。
  • 解析 JWT 时,使用 jwt.ParseWithClaims 来验证 JWT 的签名并提取负载信息。
  • JWT 认证可以用于保护 API 接口,确保只有通过身份验证的用户能够访问特定资源。

标题:golang实现jwt
作者:mooncakeee
地址:http://blog.dd95828.com/articles/2025/01/15/1736923299793.html
联系:scotttu@163.com