JWT认证 #

一、JWT概述 #

1.1 JWT结构 #

JWT由三部分组成:Header.Payload.Signature

text
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IuW8oOS4iSIsImlhdCI6MTUxNjIzOTAyMn0.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

1.2 JWT流程 #

text
1. 用户登录 → 服务器验证
2. 验证成功 → 生成JWT
3. 返回JWT → 客户端存储
4. 后续请求 → 携带JWT
5. 服务器验证JWT → 返回数据

二、安装JWT库 #

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

三、Token生成 #

3.1 定义Claims #

go
type JwtClaims struct {
    UserID uint   `json:"user_id"`
    Name   string `json:"name"`
    Role   string `json:"role"`
    jwt.RegisteredClaims
}

3.2 生成Token #

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

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

3.3 登录接口 #

go
type LoginRequest struct {
    Username string `json:"username" validate:"required"`
    Password string `json:"password" validate:"required"`
}

e.POST("/login", func(c echo.Context) error {
    req := new(LoginRequest)
    if err := c.Bind(req); err != nil {
        return err
    }
    
    user, err := authenticate(req.Username, req.Password)
    if err != nil {
        return echo.NewHTTPError(http.StatusUnauthorized, "用户名或密码错误")
    }
    
    token, err := GenerateToken(user.ID, user.Name, user.Role)
    if err != nil {
        return err
    }
    
    return c.JSON(http.StatusOK, map[string]string{
        "token": token,
    })
})

四、Token验证 #

4.1 解析Token #

go
func ParseToken(tokenString string) (*JwtClaims, error) {
    token, err := jwt.ParseWithClaims(tokenString, &JwtClaims{}, func(token *jwt.Token) (interface{}, error) {
        return jwtSecret, nil
    })
    
    if err != nil {
        return nil, err
    }
    
    if claims, ok := token.Claims.(*JwtClaims); ok && token.Valid {
        return claims, nil
    }
    
    return nil, errors.New("invalid token")
}

4.2 JWT中间件 #

go
func JWTMiddleware(next echo.HandlerFunc) echo.HandlerFunc {
    return func(c echo.Context) error {
        auth := c.Request().Header.Get("Authorization")
        if auth == "" {
            return echo.NewHTTPError(http.StatusUnauthorized, "请登录")
        }
        
        parts := strings.Split(auth, " ")
        if len(parts) != 2 || parts[0] != "Bearer" {
            return echo.NewHTTPError(http.StatusUnauthorized, "Token格式错误")
        }
        
        claims, err := ParseToken(parts[1])
        if err != nil {
            return echo.NewHTTPError(http.StatusUnauthorized, "Token无效")
        }
        
        c.Set("userID", claims.UserID)
        c.Set("name", claims.Name)
        c.Set("role", claims.Role)
        
        return next(c)
    }
}

4.3 使用中间件 #

go
api := e.Group("/api")
api.Use(JWTMiddleware)

api.GET("/profile", getProfile)
api.GET("/users", getUsers)

五、内置JWT中间件 #

5.1 配置JWT中间件 #

go
e.Use(middleware.JWTWithConfig(middleware.JWTConfig{
    SigningKey:  []byte("secret"),
    TokenLookup: "header:Authorization:Bearer ",
    AuthScheme:  "Bearer",
    ContextKey:  "user",
}))

5.2 获取用户信息 #

go
e.GET("/profile", func(c echo.Context) error {
    user := c.Get("user").(*jwt.Token)
    claims := user.Claims.(jwt.MapClaims)
    
    return c.JSON(http.StatusOK, map[string]interface{}{
        "user_id": claims["user_id"],
        "name":    claims["name"],
        "role":    claims["role"],
    })
})

六、Token刷新 #

6.1 刷新机制 #

go
func GenerateRefreshToken(userID uint) (string, error) {
    claims := jwt.RegisteredClaims{
        Subject:   fmt.Sprintf("%d", userID),
        ExpiresAt: jwt.NewNumericDate(time.Now().Add(7 * 24 * time.Hour)),
        IssuedAt:  jwt.NewNumericDate(time.Now()),
    }
    
    token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
    return token.SignedString(jwtSecret)
}

func RefreshToken(refreshToken string) (string, error) {
    token, err := jwt.ParseWithClaims(refreshToken, &jwt.RegisteredClaims{}, func(token *jwt.Token) (interface{}, error) {
        return jwtSecret, nil
    })
    
    if err != nil || !token.Valid {
        return "", errors.New("invalid refresh token")
    }
    
    claims := token.Claims.(*jwt.RegisteredClaims)
    userID, _ := strconv.ParseUint(claims.Subject, 10, 64)
    
    user, err := getUserByID(uint(userID))
    if err != nil {
        return "", err
    }
    
    return GenerateToken(user.ID, user.Name, user.Role)
}

6.2 刷新接口 #

go
e.POST("/refresh", func(c echo.Context) error {
    refreshToken := c.FormValue("refresh_token")
    if refreshToken == "" {
        return echo.NewHTTPError(http.StatusBadRequest, "缺少refresh token")
    }
    
    token, err := RefreshToken(refreshToken)
    if err != nil {
        return echo.NewHTTPError(http.StatusUnauthorized, "refresh token无效")
    }
    
    return c.JSON(http.StatusOK, map[string]string{
        "token": token,
    })
})

七、权限控制 #

7.1 角色中间件 #

go
func RoleMiddleware(roles ...string) echo.MiddlewareFunc {
    return func(next echo.HandlerFunc) echo.HandlerFunc {
        return func(c echo.Context) error {
            role := c.Get("role").(string)
            
            for _, r := range roles {
                if role == r {
                    return next(c)
                }
            }
            
            return echo.NewHTTPError(http.StatusForbidden, "权限不足")
        }
    }
}

7.2 使用角色中间件 #

go
admin := e.Group("/admin")
admin.Use(JWTMiddleware)
admin.Use(RoleMiddleware("admin"))

admin.GET("/users", listUsers)
admin.DELETE("/users/:id", deleteUser)

7.3 权限验证 #

go
func checkPermission(c echo.Context, resource, action string) bool {
    role := c.Get("role").(string)
    
    permissions := map[string]map[string][]string{
        "admin": {
            "users": {"read", "write", "delete"},
            "posts": {"read", "write", "delete"},
        },
        "editor": {
            "posts": {"read", "write"},
        },
        "user": {
            "posts": {"read"},
        },
    }
    
    if actions, ok := permissions[role][resource]; ok {
        for _, a := range actions {
            if a == action {
                return true
            }
        }
    }
    
    return false
}

func PermissionMiddleware(resource, action string) echo.MiddlewareFunc {
    return func(next echo.HandlerFunc) echo.HandlerFunc {
        return func(c echo.Context) error {
            if !checkPermission(c, resource, action) {
                return echo.NewHTTPError(http.StatusForbidden, "权限不足")
            }
            return next(c)
        }
    }
}

八、完整示例 #

go
package main

import (
    "errors"
    "fmt"
    "net/http"
    "strings"
    "time"
    "github.com/golang-jwt/jwt/v5"
    "github.com/labstack/echo/v4"
    "github.com/labstack/echo/v4/middleware"
)

type User struct {
    ID       uint   `json:"id"`
    Username string `json:"username"`
    Password string `json:"-"`
    Name     string `json:"name"`
    Role     string `json:"role"`
}

type JwtClaims struct {
    UserID uint   `json:"user_id"`
    Name   string `json:"name"`
    Role   string `json:"role"`
    jwt.RegisteredClaims
}

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

var users = map[string]*User{
    "admin": {ID: 1, Username: "admin", Password: "admin123", Name: "管理员", Role: "admin"},
    "user":  {ID: 2, Username: "user", Password: "user123", Name: "普通用户", Role: "user"},
}

func main() {
    e := echo.New()
    
    e.Use(middleware.Logger())
    e.Use(middleware.Recover())
    
    e.POST("/login", login)
    e.POST("/refresh", refreshToken)
    
    api := e.Group("/api")
    api.Use(JWTMiddleware)
    
    api.GET("/profile", getProfile)
    
    admin := api.Group("/admin")
    admin.Use(RoleMiddleware("admin"))
    admin.GET("/users", listUsers)
    
    e.Logger.Fatal(e.Start(":8080"))
}

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

func ParseToken(tokenString string) (*JwtClaims, error) {
    token, err := jwt.ParseWithClaims(tokenString, &JwtClaims{}, 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 jwtSecret, nil
    })
    
    if err != nil {
        return nil, err
    }
    
    if claims, ok := token.Claims.(*JwtClaims); ok && token.Valid {
        return claims, nil
    }
    
    return nil, errors.New("invalid token")
}

func JWTMiddleware(next echo.HandlerFunc) echo.HandlerFunc {
    return func(c echo.Context) error {
        auth := c.Request().Header.Get("Authorization")
        if auth == "" {
            return echo.NewHTTPError(http.StatusUnauthorized, "请登录")
        }
        
        parts := strings.Split(auth, " ")
        if len(parts) != 2 || parts[0] != "Bearer" {
            return echo.NewHTTPError(http.StatusUnauthorized, "Token格式错误")
        }
        
        claims, err := ParseToken(parts[1])
        if err != nil {
            return echo.NewHTTPError(http.StatusUnauthorized, "Token无效或已过期")
        }
        
        c.Set("userID", claims.UserID)
        c.Set("name", claims.Name)
        c.Set("role", claims.Role)
        
        return next(c)
    }
}

func RoleMiddleware(roles ...string) echo.MiddlewareFunc {
    return func(next echo.HandlerFunc) echo.HandlerFunc {
        return func(c echo.Context) error {
            role := c.Get("role").(string)
            
            for _, r := range roles {
                if role == r {
                    return next(c)
                }
            }
            
            return echo.NewHTTPError(http.StatusForbidden, "权限不足")
        }
    }
}

func login(c echo.Context) error {
    type LoginRequest struct {
        Username string `json:"username"`
        Password string `json:"password"`
    }
    
    req := new(LoginRequest)
    if err := c.Bind(req); err != nil {
        return err
    }
    
    user, ok := users[req.Username]
    if !ok || user.Password != req.Password {
        return echo.NewHTTPError(http.StatusUnauthorized, "用户名或密码错误")
    }
    
    token, err := GenerateToken(user.ID, user.Name, user.Role)
    if err != nil {
        return err
    }
    
    return c.JSON(http.StatusOK, map[string]string{
        "token": token,
    })
}

func refreshToken(c echo.Context) error {
    return echo.NewHTTPError(http.StatusNotImplemented, "功能待实现")
}

func getProfile(c echo.Context) error {
    return c.JSON(http.StatusOK, map[string]interface{}{
        "user_id": c.Get("userID"),
        "name":    c.Get("name"),
        "role":    c.Get("role"),
    })
}

func listUsers(c echo.Context) error {
    return c.JSON(http.StatusOK, users)
}

九、总结 #

JWT认证要点:

要点 说明
Token生成 jwt.NewWithClaims()
Token签名 token.SignedString()
Token解析 jwt.ParseWithClaims()
JWT中间件 验证Token
角色控制 RoleMiddleware
权限验证 PermissionMiddleware

准备好学习文件上传下载了吗?让我们进入下一章!

最后更新:2026-03-28