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

news/2025/2/25 5:45:02

身份验证与授权(JWT)

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

JWT 认证流程 :

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

1.代码实现

1.1使用jwt库

安装jwt-go库:

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文件:

// 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 文件:

// 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 文件:

// 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文件:

// 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

请求参数:

{
    "username" :"admin",
    "password" :"admin123"
}

返回参数:

{"token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE3NDAzMTkyNTIsInVzZXJfaWQiOjEsInVzZXJuYW1lIjoiYWRtaW4ifQ.k8TKg4-hZj5XTi7e6A9riXTA-jmf3K3SwXrdwZnqCf4"}

在这里插入图片描述

2.2 使用token访问保护路由

访问地址:

http://localhost:8080/api/profile

添加请求头:

Authorization:Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE3NDAzMTkyNTIsInVzZXJfaWQiOjEsInVzZXJuYW1lIjoiYWRtaW4ifQ.k8TKg4-hZj5XTi7e6A9riXTA-jmf3K3SwXrdwZnqCf4

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

返回参数:

{
    "user_id": 1,
    "username": "admin"
}

在这里插入图片描述

3.使用BasicAuth中间件

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

演示代码:

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")
}


测试请求:

curl -u admin:admin123 http://localhost:8080/admin/secrets

客户端需在请求头中携带 Authorization 字段,格式为 Basic base64(username:password)
例如,用户 admin 密码 admin123 的认证头为:

Authorization: Basic YWRtaW46YWRtaW4xMjM=

使用 Authorization 来测试
在这里插入图片描述


http://www.niftyadmin.cn/n/5865040.html

相关文章

大模型架构与训练方向

一、核心知识领域 ‌模型架构设计‌ 掌握Transformer、MoE&#xff08;Mixture-of-Experts&#xff09;、RetNet等主流架构的原理与实现细节&#xff0c;需深入理解注意力机制、位置编码、稀疏激活等技术‌13。学习多模态融合架构&#xff08;如CLIP、Flamingo&#xff09;&…

Unity3D实现编辑器截屏Preview窗口,并生成图片PNG

系列文章目录 unity工具 文章目录 系列文章目录👉前言👉一、Preview窗口👉二、手动选择预制体截屏👉二、自动选择预制体截屏👉壁纸分享👉总结👉前言 想要在Unity编辑器中截取Preview窗口的截图,并生成图片的代码。 有时候可能会需要此模型的图片,又不想P图,…

AI(14)-prompt

1.BaseLLM 和Tuned LLM之间的区别 基本模型 指令微调模型 人类反强化学习 2.指南 下载包 导入包设置key 2个基本原则 写明确特定的指令 给模型时间思考 2.1.使用分割符清楚地指示输入的不同部分 示例&#xff1a;对这个段落进行总结&#xff0c;将用3个单引号…

企业财务数据分析-投资回报指标ROA

上一篇文章主要介绍了关于投资回报的核心指标ROE&#xff0c;其实还有一个比较关键的指标资产回报率指标&#xff08;ROA&#xff09;&#xff0c;资产收益率是用来衡量企业盈利能力的重要财务分析指标。资产收益率越高&#xff0c;说明企业资产的利用效率越高&#xff0c;利用…

自定义SpringBoot Starter

✅自定义SpringBoot Starter SpringBoot 的 starter 可以帮我们简化配置&#xff0c;非常的方便&#xff0c;定义起来其实也不复杂&#xff0c;我的项目中定义了很多 starter&#xff0c;比如business-job就是一个 stater&#xff0c;以他为例&#xff0c;介绍下如何定义 star…

[Web 安全] Web 安全攻防 - 学习手册

0x01&#xff1a;Web 安全攻防 —— 信息收集篇 Web 信息收集 — 手动收集域名信息Web 信息收集 — 手动收集 IP 信息Web 信息收集 — 端口服务信息收集 Web 信息收集 — 自动化信息收集 — ShuiZe 水泽Web 信息收集 — 自动化信息收集 — ARL 灯塔 0x02&#xff1a;Web 安全…

设计模式教程:状态模式(State Pattern)

一、概述 状态模式&#xff08;State Pattern&#xff09;是一种行为型设计模式&#xff0c;允许对象在内部状态发生变化时改变其行为。换句话说&#xff0c;状态模式让对象的行为随其状态变化而变化。它的主要目的是将状态相关的行为封装到状态类中&#xff0c;从而避免在类中…

23种设计模式之《外观模式(Facade)》在c#中的应用及理解

程序设计中的主要设计模式通常分为三大类&#xff0c;共23种&#xff1a; 1. 创建型模式&#xff08;Creational Patterns&#xff09; 单例模式&#xff08;Singleton&#xff09;&#xff1a;确保一个类只有一个实例&#xff0c;并提供全局访问点。 工厂方法模式&#xff0…