Go语言原生实现JWT的完整示例

Alice
Alice
发布时间:2026-10-15| 4人浏览过
原创

JWT(JSON Web Token)是一种广泛应用于系统间传输认证信息的安全令牌。在Go语言开发中,虽然有许多成熟的第三方库可用,但掌握其底层原理至关重要。本文详细展示了如何利用 Go 的标准库(如 crypto/hmac、crypto/sha256 等)原生编写 JWT 的生成与解析代码,并结合 Gin 框架提供完整的中间件鉴权示例。

目标与准备条件

我们的目标是构建一个轻量级且安全的 JWT 工具包,支持标准的 HS256 算法。无需引入任何第三方依赖,仅使用 Go 内置模块即可完成。主要涉及三个核心部分:

  • 结构体定义:包含头部(Header)、负载(Payload)和签名(Signature)。
  • 签名算法:使用 HMAC-SHA256 对前两部分内容进行加密处理。
  • 编解码逻辑:将数据转换为 Base64Url 格式进行拼接与还原。

第1步:定义JWT结构与基础工具函数

首先封装基础的 JWT 结构体和辅助函数。为了保持简洁,我们将结构体的字段设为私有。

type JWT struct {
	header    string
	Payload   string
	signature string
}

接着封装 Base64 编码方法,JWT 规范要求使用 URL 安全的原始 Base64 编码:

func encodeBase64(data string) string {
	return base64.RawURLEncoding.EncodeToString([]byte(data))
}

封装签名生成方法,使用 crypto/hmac 和 crypto/sha256:

func generateSignature(key []byte, data []byte) (string, error) {
	hash := hmac.New(sha256.New, key)
	_, err := hash.Write(data)
	if err != nil {
		return "", err
	}
	// 计算摘要并进行 Base64 编码
	return encodeBase64(string(hash.Sum(nil))), nil
}

第2步:实现Token创建(CreateToken)

此步骤负责构建合法的 JWT 字符串。流程为:序列化 Payload -> 拼接 Header.Payload -> 计算签名 -> 输出最终 Token。

func CreateToken(key []byte, payloadData any) (string, error) {
	// 1. 定义标准头部
	header := `{"alg":"HS256","typ":"JWT"}`
	
	// 2. 将负载数据序列化为 JSON
	payload, jsonErr := json.Marshal(payloadData)
	if jsonErr != nil {
		return "", fmt.Errorf("负载json解析错误")
	}
	
	// 3. Base64 编码头部和负载,并用 "." 连接
	encodedHeader := encodeBase64(header)
	encodedPayload := encodeBase64(string(payload))
	headerAndPayload := encodedHeader + "." + encodedPayload
	
	// 4. 生成签名
	signature, err := generateSignature(key, []byte(headerAndPayload))
	if err != nil {
		return "", err
	}
	
	// 5. 拼接三部分返回
	return headerAndPayload + "." + signature, nil
}

第3步:实现Token解析(ParseJwt)

解析时需要严格校验签名的有效性,防止恶意伪造的 Token。如果签名不一致或格式不对,应立即拒绝。

func ParseJwt(token string, key []byte) (*JWT, error) {
	// 1. 分割 Token 字符串
	jwtParts := strings.Split(token, ".")
	if len(jwtParts) != 3 {
		return nil, fmt.Errorf("非法token")
	}
	
	encodedHeader := jwtParts[0]
	encodedPayload := jwtParts[1]
	signature := jwtParts[2]
	
	// 2. 重新计算签名以验证合法性
	confirmSignature, err := generateSignature(key, []byte(encodedHeader+"."+encodedPayload))
	if err != nil {
		return nil, fmt.Errorf("生成签名错误")
	}
	
	// 3. 比对签名,确保 Token 未被篡改
	if signature != confirmSignature {
		return nil, fmt.Errorf("token验证失败")
	}
	
	// 4. 解码负载并返回对象
	dstPayload, _ := base64.RawURLEncoding.DecodeString(encodedPayload)
	return &JWT{encodedHeader, string(dstPayload), signature}, nil
}

实际使用与测试场景

下面结合 gin 框架构建一个简单的 API 服务,演示如何注册中间件以及不同情况下的返回结果。

准备工作:密钥与数据结构

定义用户结构体和测试用的简单密钥:

type UserInfo struct {
	Name     string `json:"name"`
	Password string `json:"password"`
}
var Key []byte = []byte("123456") // 生产环境应使用强随机密钥

配置鉴权中间件

在 Gin 路由中使用自定义中间件拦截请求,白名单排除登录接口:

func jwtConfirm(context *gin.Context) {
	if context.Request.RequestURI == "/login" {
		context.Next()
		return
	}
	token := context.GetHeader("Token")
	jwtObj, err := utils.ParseJwt(token, Key)
	if err != nil {
		context.JSON(200, gin.H{"msg": err.Error()})
		context.Abort()
		return
	}
	context.JSON(200, gin.H{"payload": jwtObj.Payload})
}

主程序路由逻辑

实现 /login 用于获取 Token,以及受保护的 /doing 接口:

func main() {
	router := gin.Default()
	router.Use(jwtConfirm)
	
	router.POST("/login", func(context *gin.Context) {
		var userInfo UserInfo
		if bindErr := context.ShouldBindJSON(&userInfo); bindErr != nil {
			context.JSON(200, gin.H{"msg": bindErr.Error()})
			return
		}
		token, err := utils.CreateToken(Key, userInfo)
		if err != nil {
			context.JSON(500, gin.H{"err": err.Error()})
			return
		}
		context.JSON(200, gin.H{"token": token})
	})
	
	router.GET("/doing", func(context *gin.Context) {
		context.JSON(200, gin.H{"msg": "访问成功"})
	})
	
	router.Run()
}

测试结果分析

我们对上述逻辑进行了四项关键测试,观察系统在不同条件下的反应:

1. 正常获取Token

向 /login 发送正确的用户名和密码后,服务端成功计算出 JWT 并返回。这证明了 CreateToken 方法生成的 Token 格式符合规范。

2. 缺失Token的请求

直接请求受保护的路由而不在 Header 中携带 Token,或者 Header 为空。此时中间件拦截该请求并反馈错误信息。这体现了鉴权机制的第一道防线。

3. 携带错误Token的情况

故意伪造一个不符合规范的 Token 字符串(例如修改 Payload 中的内容)。由于密钥不变,重新计算的签名将无法通过校验,系统将拒绝访问并提示验证失败。

4. 使用正确Token的访问

使用在第1步中获取到的真实 Token 发起请求。此时 ParseJwt 校验签名通过,解密出原始 Payload 并将负载信息成功返回给客户端,实现了正常的业务流转。

简要总结

在实际的工程环境中,通常会增加更复杂的参数设置,例如在 Payload 中加入 exp(过期时间)以提高安全性,密钥也会更加复杂以防破解。但无论复杂度如何变化,核心的 Token 生成流程和解析验证操作均遵循上述逻辑。

以上就是用Go语言原生实现JWT的详细内容,更多关于Go语言原生实现JWT的内容请搜索本站以前的文章或继续浏览下面的相关文章希望大家以后多多支持本站!

免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。

Python 相关产品和应用

全部下载
Selenium

Selenium

4.51.0 · Windows

用于浏览器自动化和 Web 测试的 Python 工具。

scikit-learn

scikit-learn

1.9.1 · Windows

常用 Python 机器学习算法与模型评估工具库。

Black

Black

26.10.1 · Windows

强调一致性的 Python 自动代码格式化工具。

Ruff

Ruff

0.17.1 · macOS

由 Rust 编写的高速 Python 代码检查和格式化工具。

pytest

pytest

9.1.1 · Windows

简洁而强大的 Python 自动化测试框架。

FastAPI

FastAPI

0.146.0 · Windows

高性能 Python API 框架,支持类型提示和自动接口文档。

Flask

Flask

3.1.3 · Windows

轻量灵活的 Python Web 应用框架。

Django

Django

6.1.2 · Windows

成熟的 Python Web 框架,适合构建内容站点和业务系统。

Spyder

Spyder

6.1.7 · Windows

面向科学计算与数据分析的 Python 集成开发环境。

SciPy

SciPy

1.18.1 · Windows

面向科学、工程和技术计算的 Python 算法库。