golang原生实现JWT的示例代码

作者:coderDreams 时间:2024-02-08 05:59:51 

JWT(JSON Web Token)是一种基于JSON的安全令牌,可以用于在不同系统之间传输认证信息。在Go中实现JWT验证,可以通过标准库crypto/hmaccrypto/sha256encoding/base64来编写自己的JWT。

获取Token

我们在此封装一个JWT的struct结构体(由于除了Payload,其他很大可能不会在其他地方用到,所以不公开)

type JWT struct {
   header    string
   Payload   string
   signature string
}

将base64的编码封装一下方便使用

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

我们封装一个用来生成签名的方法

func generateSignature(key []byte, data []byte) (string, error) {
// 创建一个哈希对象
hash := hmac.New(sha256.New, key)
// 将要签名的信息写入哈希对象中 hash.Write(data)
_, err := hash.Write(data)
if err != nil {
return "", err
}
// hash.Sum()计算签名,在这里会返回签名内容
// 将签名经过base64编码生成字符串形式返回。
return encodeBase64(string(hash.Sum(nil))), nil
}

我们封装一个CreateToken用于生成Token(该方法的参数key为(生成签名所使用的密钥))

func CreateToken(key []byte, payloadData any) (string, error) {
   // 标准头部
header := `{"alg":"HS256","typ":"JWT"}`
   // 将负载的数据转换为json
payload, jsonErr := json.Marshal(payloadData)
if jsonErr != nil {
return "", fmt.Errorf("负载json解析错误")
}
   // 将头部和负载通过base64编码,并使用.作为分隔进行连接
encodedHeader := encodeBase64(header)
encodedPayload := encodeBase64(string(payload))
HeaderAndPayload := encodedHeader + "." + encodedPayload
   // 使用签名使用的key将传入的头部和负载连接所得的数据进行签名
signature, err := generateSignature(key, []byte(HeaderAndPayload))
if err != nil {
return "", err
}
   // 将token的三个部分使用.进行连接并返回
return HeaderAndPayload + "." + signature, nil
}

解析Token

我们封装一个解析token的方法

func ParseJwt(token string, key []byte) (*JWT, error) {
   // 分解规定,我们使用.进行分隔,所以我们通过.进行分隔成三个字符串的数组
jwtParts := strings.Split(token, ".")
   // 数据数组长度不是3就说明token在格式上就不合法
if len(jwtParts) != 3 {
return nil, fmt.Errorf("非法token")
}
// 分别拿出
encodedHeader := jwtParts[0]
encodedPayload := jwtParts[1]
signature := jwtParts[2]
// 使用key将token中的头部和负载用.连接后进行签名
   // 这个签名应该个token中第三部分的签名一致
confirmSignature, err := generateSignature(key, []byte(encodedHeader+"."+encodedPayload))
if err != nil {
return nil, fmt.Errorf("生成签名错误")
}
   // 如果不一致
if signature != confirmSignature {
return nil, fmt.Errorf("token验证失败")
}
// 将payload解base64编码
dstPayload, _ := base64.RawURLEncoding.DecodeString(encodedPayload)
   // 返回我们的JWT对象以供后续使用
return &JWT{encodedHeader, string(dstPayload), signature}, nil
}

实际使用

我们构造一个用户的结构体

type UserInfo struct {
Name     string `json:"name"`
Password string `json:"password"`
}

我们这次使用123456作为密钥简单的验证一下

var Key []byte = []byte("12346")

在此我们构造一个验证的中间件

func jwtConfirm(context *gin.Context) {
// 登录不需要token
if context.Request.RequestURI == "/login" {
return
}
// 拿出token
token := context.GetHeader("Token")
   // 进行解析验证
jwt, err := utils.ParseJwt(token, Key)
if err != nil {
context.JSON(200, gin.H{
"msg": err.Error(),
})
       // 有问题就流产掉(我也不知道怎么翻译好了,香蕉猫.jpg)
context.Abort()
}
   // 验证通过就将负载返回回去
context.JSON(200, gin.H{
"payload": jwt.Payload,
})
}

我们在此基础上就可以使用了,这边使用gin框架简单的测试一下

func main() {
   // 使用默认路由
router := gin.Default()
   // 注册中间件
router.Use(jwtConfirm)
   // 简单做两个服务
router.POST("/login", func(context *gin.Context) {
// 接收用户参数
var userInfo UserInfo = UserInfo{}
       // 使用jsonbind接收
bindErr := context.ShouldBindJSON(&userInfo)
if bindErr != nil {
context.JSON(200, gin.H{
"msg": bindErr.Error(),
})
}
// 使用密钥做出token
jwt, err := utils.CreateJwt(Key, userInfo)
if err != nil {
fmt.Println(err)
}
        // 我们将token直接返回用于测试
context.JSON(200, gin.H{
"token": jwt,
})
})
router.GET("/doing")
router.Run()
}

测试结果

golang原生实现JWT的示例代码

我们拿到了token

我们现在去试一下如果不带token的结果

golang原生实现JWT的示例代码

我们试一下携带错误token的情况

golang原生实现JWT的示例代码

我们最后测试一下正确的token

golang原生实现JWT的示例代码

来源:https://juejin.cn/post/7227377854039654459

标签:golang,JWT
0
投稿

猜你喜欢

  • C#调用Python脚本的简单示例

    2021-04-03 13:22:25
  • js实现本地持久化存储登录注册

    2024-04-16 10:35:05
  • Centos7下使用yum安装mysql数据库的详细教程(增强版)

    2024-01-13 17:24:52
  • vue 单页应用和多页应用的优劣

    2024-04-30 10:44:18
  • 永久解决VSCode终端中文乱码问题

    2023-09-18 14:47:57
  • python数据库操作常用功能使用详解(创建表/插入数据/获取数据)

    2024-01-25 20:06:36
  • Vue 实现从小到大的横向滑动效果详解

    2024-05-10 14:14:10
  • python对象及面向对象技术详解

    2023-05-14 00:04:16
  • Vue.js每天必学之过滤器与自定义过滤器

    2024-05-09 10:41:36
  • Python如何实现强制数据类型转换

    2022-10-18 10:08:02
  • pandas读取csv文件提示不存在的解决方法及原因分析

    2022-10-27 19:08:02
  • MySQL分区表的最佳实践指南

    2024-01-27 14:45:40
  • mysql之TIMESTAMP(时间戳)用法详解

    2024-01-13 16:06:42
  • numpy中的log和ln函数解读

    2023-06-14 22:46:40
  • Python图片处理模块PIL操作方法(pillow)

    2021-11-11 18:19:59
  • 解决python3中cv2读取中文路径的问题

    2023-05-17 18:52:10
  • 简单介绍Python虚拟环境及使用方法

    2021-03-19 20:59:53
  • 如何用Python制作微信好友个性签名词云图

    2023-02-24 07:46:20
  • BERT vs GPT自然语言处理中的关键差异详解

    2022-04-01 08:15:36
  • MySQL数据库优化详解

    2024-01-23 12:51:55
  • asp之家 网络编程 m.aspxhome.com