golang原生实现JWT的示例代码
作者:coderDreams 发布时间:2024-02-08 05:59:51
标签:golang,JWT
JWT(JSON Web Token)是一种基于JSON的安全令牌,可以用于在不同系统之间传输认证信息。在Go中实现JWT验证,可以通过标准库crypto/hmac
、crypto/sha256
和encoding/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()
}
测试结果
我们拿到了token
我们现在去试一下如果不带token的结果
我们试一下携带错误token的情况
我们最后测试一下正确的token
来源:https://juejin.cn/post/7227377854039654459
0
投稿
猜你喜欢
- 前言在最一开始,我的B站收藏一直是存放在默认收藏夹中,但是随着视频收藏的越来越多,没有分类的视频放在一起,想在众多视频中找到想要的视频非常困
- 前言何为爬虫,其实就是利用计算机模拟人对网页的操作例如 模拟人类浏览购物网站使用爬虫前一定要看目标网站可刑不可刑 :-)可以在目标网站添加/
- 一 使用SELECT子句进行多表查询SELECT 字段名 FROM 表1,表2 …&nbs
- ipython简介ipython他是一个非常流行的python解释器,相比于原生的python解释器,有太多优点和长处,因此几乎是pytho
- 大家好,欢迎大家来到算法数据结构专题,今天我们和大家聊一个非常常用的算法,叫做LRU。LRU的英文全称是Least Recently Use
- 在python中加背景音乐的方法:1、导入pygame资源包;2、修改音乐的file路径;3、使用init()方法进行初始化;4、使用loa
- 关于缓存剩下的问题是数据的隐私性以及在级联缓存中数据应该在何处储存的问题。通常用户将会面对两种缓存: 他或她自己的浏览器缓存(私有缓存)以及
- 一、 [::-1]import numpy as npimport numpy as npx = np.arange(1, 6)print(
- 概述Mysql的Replication(复制)是一个异步的复制过程,从一个 Mysql instance(我们称之为 Master)复制到另
- Linux服务器有CentOS、Fedora等,都预先安装了Python,版本从2.4到2.5不等,而Windows类型的服务器也多数安装了
- 最近游戏项目在多个国家上线,每个国家都对应两份儿svn目录(一份是本地策划目录,一份是线上目录)。于是乎维护变得很烦躁。需要先更新本地策划s
- 各位想必都知道,onfocus="this.blur()"这条代码能消除链接时的虚线框,但你有没有想过,如果你的网页上有
- #mysqldump --help1.mysqldump的几种常用方法:(1)导出整个数据库(包括数据库中的数据)mysqldump -u
- 这篇文章主要介绍了python matplotlib折线图样式实现过程,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学
- 官方实现golang 1.8 及以上版本提供了一个创建共享库(shared object)的新工具,称为 Plugins。目前 Plugin
- 自己写的方法,适用于linux,#!/usr/bin/python#coding=utf-8import sysimport os, os.
- SQL触发器实例1 定义: 何为触发器?在SQL Server里面也就是对某一个表的一定的操作,触发某种条件,从而执行的一段程序。触发器是一
- 一开始用phpMyAdmin来执行,后来出现一堆错误,后来去掉了begin,end之后可以正常执行,但要执行存储过程,在phpMyAdmn中
- 在asp中调用sql server的存储过程可以加快程序运行速度,本文介绍了asp使用存储过程的方法。1.调用存储过程的一般方法 先假设在s
- 假如某个电脑生产商,它的数据库中保存着整机和配件的产品信息。用来保存整机产品信息的表叫做pc;用来保存配件供货信息的表叫做parts。在pc