JWT认证 #

一、JWT概述 #

1.1 什么是JWT #

JWT(JSON Web Token)是一种开放标准,用于在各方之间安全传输信息。

1.2 JWT结构 #

text
Header.Payload.Signature

示例:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoxfQ.abc123

二、安装依赖 #

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

三、Token生成 #

3.1 基本生成 #

go
package main

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

var jwtSecret = []byte("your-secret-key")

type Claims struct {
    UserID string `json:"user_id"`
    Role   string `json:"role"`
    jwt.RegisteredClaims
}

func GenerateToken(userID, role string) (string, error) {
    claims := Claims{
        UserID: userID,
        Role:   role,
        RegisteredClaims: jwt.RegisteredClaims{
            ExpiresAt: jwt.NewNumericDate(time.Now().Add(24 * time.Hour)),
            IssuedAt:  jwt.NewNumericDate(time.Now()),
            Issuer:    "my-app",
        },
    }
    
    token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
    return token.SignedString(jwtSecret)
}

3.2 登录接口 #

go
app.Post("/login", func(c *fiber.Ctx) error {
    type LoginInput struct {
        Username string `json:"username"`
        Password string `json:"password"`
    }
    
    var input LoginInput
    if err := c.BodyParser(&input); err != nil {
        return c.Status(400).JSON(fiber.Map{
            "error": "Invalid input",
        })
    }
    
    // 验证用户(示例)
    if input.Username != "admin" || input.Password != "password" {
        return c.Status(401).JSON(fiber.Map{
            "error": "Invalid credentials",
        })
    }
    
    // 生成Token
    token, err := GenerateToken("1", "admin")
    if err != nil {
        return c.Status(500).JSON(fiber.Map{
            "error": "Failed to generate token",
        })
    }
    
    return c.JSON(fiber.Map{
        "token": token,
    })
})

四、Token验证 #

4.1 验证中间件 #

go
func JWTMiddleware() fiber.Handler {
    return func(c *fiber.Ctx) error {
        authHeader := c.Get("Authorization")
        if authHeader == "" {
            return c.Status(401).JSON(fiber.Map{
                "error": "Missing authorization header",
            })
        }
        
        // 解析Bearer Token
        parts := strings.Split(authHeader, " ")
        if len(parts) != 2 || parts[0] != "Bearer" {
            return c.Status(401).JSON(fiber.Map{
                "error": "Invalid authorization format",
            })
        }
        
        tokenString := parts[1]
        
        // 解析Token
        token, err := jwt.ParseWithClaims(tokenString, &Claims{}, func(token *jwt.Token) (interface{}, error) {
            return jwtSecret, nil
        })
        
        if err != nil || !token.Valid {
            return c.Status(401).JSON(fiber.Map{
                "error": "Invalid token",
            })
        }
        
        // 获取Claims
        claims, ok := token.Claims.(*Claims)
        if !ok {
            return c.Status(401).JSON(fiber.Map{
                "error": "Invalid claims",
            })
        }
        
        // 存储用户信息
        c.Locals("user_id", claims.UserID)
        c.Locals("role", claims.Role)
        
        return c.Next()
    }
}

4.2 使用中间件 #

go
protected := app.Group("/api", JWTMiddleware())

protected.Get("/profile", func(c *fiber.Ctx) error {
    userID := c.Locals("user_id").(string)
    role := c.Locals("role").(string)
    
    return c.JSON(fiber.Map{
        "user_id": userID,
        "role":    role,
    })
})

五、Token刷新 #

5.1 刷新Token #

go
app.Post("/refresh", JWTMiddleware(), func(c *fiber.Ctx) error {
    userID := c.Locals("user_id").(string)
    role := c.Locals("role").(string)
    
    // 生成新Token
    newToken, err := GenerateToken(userID, role)
    if err != nil {
        return c.Status(500).JSON(fiber.Map{
            "error": "Failed to refresh token",
        })
    }
    
    return c.JSON(fiber.Map{
        "token": newToken,
    })
})

六、完整示例 #

go
package main

import (
    "strings"
    "time"
    
    "github.com/gofiber/fiber/v2"
    "github.com/golang-jwt/jwt/v4"
)

var jwtSecret = []byte("your-secret-key")

type Claims struct {
    UserID string `json:"user_id"`
    Role   string `json:"role"`
    jwt.RegisteredClaims
}

func GenerateToken(userID, role string) (string, error) {
    claims := Claims{
        UserID: userID,
        Role:   role,
        RegisteredClaims: jwt.RegisteredClaims{
            ExpiresAt: jwt.NewNumericDate(time.Now().Add(24 * time.Hour)),
            IssuedAt:  jwt.NewNumericDate(time.Now()),
        },
    }
    
    token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
    return token.SignedString(jwtSecret)
}

func JWTMiddleware() fiber.Handler {
    return func(c *fiber.Ctx) error {
        authHeader := c.Get("Authorization")
        if authHeader == "" {
            return c.Status(401).JSON(fiber.Map{"error": "Unauthorized"})
        }
        
        parts := strings.Split(authHeader, " ")
        if len(parts) != 2 || parts[0] != "Bearer" {
            return c.Status(401).JSON(fiber.Map{"error": "Invalid format"})
        }
        
        token, err := jwt.ParseWithClaims(parts[1], &Claims{}, func(t *jwt.Token) (interface{}, error) {
            return jwtSecret, nil
        })
        
        if err != nil || !token.Valid {
            return c.Status(401).JSON(fiber.Map{"error": "Invalid token"})
        }
        
        claims := token.Claims.(*Claims)
        c.Locals("user_id", claims.UserID)
        c.Locals("role", claims.Role)
        
        return c.Next()
    }
}

func main() {
    app := fiber.New()
    
    app.Post("/login", func(c *fiber.Ctx) error {
        var input struct {
            Username string `json:"username"`
            Password string `json:"password"`
        }
        
        if err := c.BodyParser(&input); err != nil {
            return c.Status(400).JSON(fiber.Map{"error": "Invalid input"})
        }
        
        if input.Username != "admin" || input.Password != "password" {
            return c.Status(401).JSON(fiber.Map{"error": "Invalid credentials"})
        }
        
        token, _ := GenerateToken("1", "admin")
        return c.JSON(fiber.Map{"token": token})
    })
    
    protected := app.Group("/api", JWTMiddleware())
    protected.Get("/profile", func(c *fiber.Ctx) error {
        return c.JSON(fiber.Map{
            "user_id": c.Locals("user_id"),
            "role":    c.Locals("role"),
        })
    })
    
    app.Listen(":3000")
}

七、总结 #

7.1 JWT认证流程 #

text
1. 用户登录 → 验证凭据
2. 生成Token → 返回给客户端
3. 客户端存储Token
4. 请求携带Token → 验证Token
5. 返回受保护资源

7.2 下一步 #

现在你已经掌握了JWT认证,接下来让我们学习 CORS跨域,了解跨域处理!

最后更新:2026-03-28