Gin从入门到精通(八)身份验证与授权(JWT)

身份验证与授权(JWT)

Gin 支持通过 JWT(JSON Web Token)来实现用户的身份验证。通过中间件验证用户的身份后,可以控制资源的访问权限。

JWT 认证流程 :

1. 用户登录 → 服务端验证 → 生成 JWT → 返回 Token  
2. 客户端存储 Token → 后续请求携带 Token  
3. 服务端验证 Token → 允许/拒绝访问  

1.代码实现

1.1使用jwt库

安装jwt-go库:

bash 复制代码
go get  github.com/dgrijalva/jwt-go

1.2 项目结构

创建如下的项目结构:

.
├── main.go
├── models/
│   └── user.go
├── handlers/
│   ├── auth.go
│   └── user.go
└── middleware/
    └── jwt.go

1.3定义用户模型与密钥

修改models/user.go文件:

go 复制代码
// models/user.go
type User struct {
    ID       uint   `json:"id"`
    Username string `json:"username" binding:"required"`
    Password string `json:"password" binding:"required"`
}

// 示例用户数据(实际应从数据库读取)
var Users = []User{
    {ID: 1, Username: "admin", Password: "admin123"},
}

// 定义 JWT 密钥(需保密,生产环境应从配置读取)
var JwtSecret = []byte("your-secret-key") // 值需要8位以上

1.4 用户登录与生成 JWT

修改 handlers/auth.go 文件:

go 复制代码
// handlers/auth.go
package handlers

import (
    "net/http"
    "time"
    "github.com/gin-gonic/gin"
	"github.com/dgrijalva/jwt-go"
    "JwtDemo/models"
)

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

func Login(c *gin.Context) {
    var req LoginRequest
    if err := c.ShouldBindJSON(&req); err != nil {
        c.JSON(http.StatusBadRequest, gin.H{"error": "参数错误"})
        return
    }

    // 校验用户(示例代码,实际应查询数据库)
    var user *models.User
    for _, u := range models.Users {
        if u.Username == req.Username && u.Password == req.Password {
            user = &u
            break
        }
    }
    if user == nil {
        c.JSON(http.StatusUnauthorized, gin.H{"error": "用户名或密码错误"})
        return
    }

    // 生成 JWT
    token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
        "user_id":  user.ID,
        "username": user.Username,
        "exp":      time.Now().Add(time.Hour * 24).Unix(), // 过期时间 24 小时
    })

    tokenString, err := token.SignedString(models.JwtSecret)
    if err != nil {
        c.JSON(http.StatusInternalServerError, gin.H{"error": "生成 Token 失败"})
        return
    }

    c.JSON(http.StatusOK, gin.H{"token": tokenString})
}

1.5编写 JWT 验证中间件

修改middleware/jwt.go 文件:

go 复制代码
// middleware/jwt.go
package middleware

import (
    "net/http"
    "strings"
  	"github.com/dgrijalva/jwt-go"
    "JwtDemo/models"
)

func JWTAuth() gin.HandlerFunc {
    return func(c *gin.Context) {
        // 从 Header 中获取 Token
        authHeader := c.GetHeader("Authorization")
        if authHeader == "" {
            c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "未提供 Token"})
            return
        }

        // 提取 Token(格式:Bearer <token>)
        parts := strings.Split(authHeader, " ")
        if len(parts) != 2 || parts[0] != "Bearer" {
            c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "Token 格式错误"})
            return
        }
        tokenString := parts[1]

        // 解析并验证 Token
        token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
            if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
                return nil, jwt.ErrSignatureInvalid
            }
            return models.JwtSecret, nil
        })

        if err != nil || !token.Valid {
            c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "无效的 Token"})
            return
        }

        // 将 Claims 存入上下文
        if claims, ok := token.Claims.(jwt.MapClaims); ok {
            c.Set("user_id", claims["user_id"])
            c.Set("username", claims["username"])
        }

        c.Next()
    }
}

1.6注册路由与使用中间件

修改main.go文件:

go 复制代码
// main.go
package main

import (
    "JwtDemo/handlers"
    "JwtDemo/middleware"
    "github.com/gin-gonic/gin"
)

func main() {
    r := gin.Default()

    // 公开路由:登录
    r.POST("/login", handlers.Login)

    // 受保护路由组
    protected := r.Group("/api")
    protected.Use(middleware.JWTAuth())
    {
        protected.GET("/profile", func(c *gin.Context) {
            userID, _ := c.Get("user_id")
            username, _ := c.Get("username")
            c.JSON(200, gin.H{
                "user_id":  userID,
                "username": username,
            })
        })
    }

    r.Run(":8080")
}

2.测试接口

2.1模拟登录获取Token

访问地址:

http://localhost:8080/login

请求参数:

json 复制代码
{
    "username" :"admin",
    "password" :"admin123"
}

返回参数:

json 复制代码
{"token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE3NDAzMTkyNTIsInVzZXJfaWQiOjEsInVzZXJuYW1lIjoiYWRtaW4ifQ.k8TKg4-hZj5XTi7e6A9riXTA-jmf3K3SwXrdwZnqCf4"}

2.2 使用token访问保护路由

访问地址:

http://localhost:8080/api/profile

添加请求头:

json 复制代码
Authorization:Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE3NDAzMTkyNTIsInVzZXJfaWQiOjEsInVzZXJuYW1lIjoiYWRtaW4ifQ.k8TKg4-hZj5XTi7e6A9riXTA-jmf3K3SwXrdwZnqCf4

注意:jwt的传递是在HTTP请求添加名为Authorization的header,形式如下 Authorization: Bearer <token> ,注意 Bearer <token> 之间有空格

返回参数:

json 复制代码
{
    "user_id": 1,
    "username": "admin"
}

3.使用BasicAuth中间件

BasicAuth 是 Gin 框架内置的中间件,用于实现 HTTP 基本认证(Basic Authentication)。它通过用户名和密码保护指定的路由,适用于简单的权限控制场景(如管理后台、内部工具等)。

演示代码:

go 复制代码
package main

import (
	"encoding/base64"
	"github.com/gin-gonic/gin"
	"net/http"
)

func main() {
	router := gin.Default()

	// 定义合法账户
	//	gin.Accounts 是 map[string]string 的一种快捷方式
	accounts := gin.Accounts{
		"admin": "admin123",
		"user":  "user123",
	}

	// 创建 BasicAuth 中间件
	auth := gin.BasicAuth(accounts)

	// 受保护的路由组
	admin := router.Group("/admin", auth)
	{
		admin.GET("/secrets", func(c *gin.Context) {
			// 获取用户,它是由 BasicAuth 中间件设置的
			user := c.MustGet(gin.AuthUserKey).(string)
			//fmt.Println(c.MustGet("Authorization").(string))
			if secret, ok := accounts[user]; ok {
				c.JSON(http.StatusOK, gin.H{"user": user, "secret": secret, "auth": base64.StdEncoding.EncodeToString([]byte(user + ":" + accounts[user]))})
			} else {
				c.JSON(http.StatusOK, gin.H{"user": user, "secret": "NO SECRET :("})
			}
		})
	}

	// 公开路由
	router.GET("/", func(c *gin.Context) {
		c.String(200, "首页(无需认证)")
	})

	router.Run(":8080")
}

测试请求:

bash 复制代码
curl -u admin:admin123 http://localhost:8080/admin/secrets

客户端需在请求头中携带 Authorization 字段,格式为 Basic base64(username:password)

例如,用户 admin 密码 admin123 的认证头为:

Authorization: Basic YWRtaW46YWRtaW4xMjM=

使用 Authorization 来测试

相关推荐
Clown955 小时前
Gin从入门到精通 (七)文件上传和下载
gin
rkmhr_sef6 小时前
Go-Gin Web 框架完整教程
前端·golang·gin
Golinie6 小时前
【Gin-Web】Bluebell社区项目梳理6:限流策略-漏桶与令牌桶
web·gin·令牌桶算法·限流策略
江禾藜8 小时前
rgin命令行工具--一键生成gin框架的开发脚手架
go·gin·命令行
Clown9520 小时前
Gin从入门到精通 (二)多种格式数据输出
gin
meowrain10 天前
Go语言 Web框架Gin
前端·golang·gin
Golinie10 天前
【Gin】Web框架开发快速入门
golang·web·gin·后端开发
莫忘初心丶12 天前
Golang Gin框架获取JSON输入
golang·json·gin
m0_7482482319 天前
Go-Gin Web 框架完整教程
前端·golang·gin