Golang 一日一库之jwt-go

这篇具有很好参考价值的文章主要介绍了Golang 一日一库之jwt-go。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

本文地址 https://www.cnblogs.com/zichliang/p/17303759.html

github地址:https://github.com/dgrijalva/jwt-go

何为 jwt token?

Golang 一日一库之jwt-go

什么是JSON Web Token?
JSON Web Token(JWT)是一个开放标准(RFC 7519),它定义了一种紧凑且自包含的方式,用于在各方之间以JSON方式安全地传输信息。由于此信息是经过数字签名的,因此可以被验证和信任。可以使用秘密(使用HMAC算法)或使用RSA或ECDSA的公钥/私钥对对JWT进行签名。
直白的讲jwt就是一种用户认证(区别于session、cookie)的解决方案。

jwt的优势与劣势

优点:

  1. 多语言支持
  2. 通用性好,不存在跨域问题
  3. 数据签名相对安全。
  4. 不需要服务端集中维护token信息,便于扩展。

缺点:
1、用户无法主动登出,只要token在有效期内就有效。这里可以考虑redis设置同token有效期一直的黑名单解决此问题。

2、token过了有效期,无法续签问题。可以考虑通过判断旧的token什么时候到期,过期的时候刷新token续签接口产生新token代替旧token

JWT的构成

Header

Header是头部
Jwt的头部承载两部分信息:
声明类型,这里是jwt
声明加密的算法 通常直接使用 HMAC SHA256

Playload(载荷又称为Claim)

playload可以填充两种类型数据
简单来说就是 比如用户名、过期时间等,

  1. 标准中注册的声明

iss: 签发者
sub: 面向的用户
aud: 接收方
exp: 过期时间
nbf: 生效时间
iat: 签发时间
jti: 唯一身份标识

  1. 自定义声明

Signature(签名)

是由header、payload 和你自己维护的一个 secret 经过加密得来的
签名的算法:

HMACSHA256(
    base64UrlEncode(header) + "." +
    base64UrlEncode(payload),
    secret
)

golang-jwt/jwt

安装

go get -u github.com/golang-jwt/jwt/v4

这里注意 **最新版是V5 但是我们使用的V4, V5 的用法 也一样 不过需要实现Claims的接口方法 一共有六个左右。并且更加严谨了 **

注册声明结构体

注册声明是JWT声明集的结构化版本,仅限于注册声明名称

type JwtCustomClaims struct {
	ID   int
	Name string
	jwt.RegisteredClaims
}

生成Token

首先需要初始化Clamins 其次在初始化结构体中注册并且设置好过期时间 主题 以及生成时间等等。。
然后会发现 jwt.RegisteredClaims
在这个方法中 还需要实现Claims接口 还需要定义几个方法
Golang 一日一库之jwt-go
如上图所示
然后我们使用
使用HS256 的签名加密方法使用指定的签名方法和声明创建一个新的[Token]
代码如下

// 本文地址 https://www.cnblogs.com/zichliang/p/17303759.html
// GenerateToken 生成Token
func GenerateToken(id int, name string) (string, error) {
	// 初始化
	iJwtCustomClaims := JwtCustomClaims{
		ID:   id,
		Name: name,
		RegisteredClaims: jwt.RegisteredClaims{
			// 设置过期时间 在当前基础上 添加一个小时后 过期
			ExpiresAt: jwt.NewNumericDate(time.Now().Add(viper.GetDuration("jwt.TokenExpire") * time.Millisecond)),
			// 颁发时间 也就是生成时间
			IssuedAt: jwt.NewNumericDate(time.Now()),
			//主题
			Subject: "Token",
		},
	}
	
	token := jwt.NewWithClaims(jwt.SigningMethodHS256, iJwtCustomClaims)
	return token.SignedString(stSignKey)
}

还有一个小坑 这里的stsignKey 必须是byte字节的
所以我们在设置签名秘钥 必须要使用byte强转
Golang 一日一库之jwt-go
像这个样子。

然后我们去执行
传入一个ID 和一个name

token, _ := utils.GenerateToken(1, "张三")
fmt.Println(token)

Golang 一日一库之jwt-go
得到如下值
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJJRCI6MSwiTmFtZSI6IuW8oOS4iSIsIlJlZ2lzdGVyZWRDbGFpbXMiOnsic3ViIjoiVG9rZW4iLCJleHAiOjE2ODExODI2MDYsImlhdCI6MTY4MTE4MjYwNn19.AmOf60S2xby6GmlGgNo4Q5b01cRoAqXWhGorzxbJ2-Q

解析Token

https://jwt.io/
在写代码之前,我们把上面的token丢到上面网站中解析一下
Golang 一日一库之jwt-go
可以发现 有三部分被解析出来了

  1. Header 告诉我们用的是什么算法,类型是什么
  2. PayLoad 我们自定义的一些数据
  3. Signature 之后服务器解析做的签名验证

代码解析token

  1. 声明一个空的数据声明
  2. 调用 jwt.ParseWithClaims 方法
  3. 传入token 数据声明接口,
  4. 判断Token是否有效
  5. 返回token
// ParseToken 解析token
func ParseToken(tokenStr string) (JwtCustomClaims, error) {
	// 声明一个空的数据声明
	iJwtCustomClaims := JwtCustomClaims{}
	//ParseWithClaims是NewParser().ParseWithClaims()的快捷方式
	//第一个值是token ,
	//第二个值是我们之后需要把解析的数据放入的地方,
	//第三个值是Keyfunc将被Parse方法用作回调函数,以提供用于验证的键。函数接收已解析但未验证的令牌。
	token, err := jwt.ParseWithClaims(tokenStr, &iJwtCustomClaims, func(token *jwt.Token) (interface{}, error) {
		return stSignKey, nil
	})

	// 判断 是否为空 或者是否无效只要两边有一处是错误 就返回无效token
	if err != nil && !token.Valid {
		err = errors.New("invalid Token")
	}
	return iJwtCustomClaims, err
}

返回成功如下图所示
Golang 一日一库之jwt-go

由于我们主动抛了个错,那我们如果手动传入错的token 看他是否会抛出错误提示呢?

jwtCustomClaim, err := utils.ParseToken(token + "12312323123")

结果:
Golang 一日一库之jwt-go

答案是会。

完整代码

package utils

import (
	"errors"
	"fmt"
	"github.com/golang-jwt/jwt/v4"
	"github.com/spf13/viper"
	"time"
)

// 把签发的秘钥 抛出来
var stSignKey = []byte(viper.GetString("jwt.SignKey"))

// JwtCustomClaims 注册声明是JWT声明集的结构化版本,仅限于注册声明名称
type JwtCustomClaims struct {
	ID               int
	Name             string
	RegisteredClaims jwt.RegisteredClaims
}

func (j JwtCustomClaims) Valid() error {
	return nil
}

// GenerateToken 生成Token
func GenerateToken(id int, name string) (string, error) {
	// 初始化
	iJwtCustomClaims := JwtCustomClaims{
		ID:   id,
		Name: name,
		RegisteredClaims: jwt.RegisteredClaims{
			// 设置过期时间 在当前基础上 添加一个小时后 过期
			ExpiresAt: jwt.NewNumericDate(time.Now().Add(viper.GetDuration("jwt.TokenExpire") * time.Minute)),
			// 颁发时间 也就是生成时间
			IssuedAt: jwt.NewNumericDate(time.Now()),
			//主题
			Subject: "Token",
		},
	}
	token := jwt.NewWithClaims(jwt.SigningMethodHS256, iJwtCustomClaims)
	return token.SignedString(stSignKey)
}

// ParseToken 解析token
func ParseToken(tokenStr string) (JwtCustomClaims, error) {
	iJwtCustomClaims := JwtCustomClaims{}
	//ParseWithClaims是NewParser().ParseWithClaims()的快捷方式
	token, err := jwt.ParseWithClaims(tokenStr, &iJwtCustomClaims, func(token *jwt.Token) (interface{}, error) {
		return stSignKey, nil
	})

	if err == nil && !token.Valid {
		err = errors.New("invalid Token")
	}
	return iJwtCustomClaims, err
}

func IsTokenValid(tokenStr string) bool {
	_, err := ParseToken(tokenStr)
	fmt.Println(err)
	if err != nil {
		return false
	}
	return true
}

dgrijalva/jwt-go

安装

go get -u "github.com/dgrijalva/jwt-go"

生成JWT

这里需要传入用户名和密码
然后根据SHA256 去进行加密 从而吧payload生成token

// 本文地址 https://www.cnblogs.com/zichliang/p/17303759.html
func Macke(user *Userinfo) (token string, err error) {
	claims := jwt.MapClaims{ //创建一个自己的声明
		"name": user.Username,
		"pwd":  user.Password,
		"iss":  "lva",
		"nbf":  time.Now().Unix(),
		"exp":  time.Now().Add(time.Second * 4).Unix(),
		"iat":  time.Now().Unix(),
	}

	then := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)

	token, err = then.SignedString([]byte("gettoken"))

	return
}

制定解析规则

Golang 一日一库之jwt-go
在自己写的这个函数中 我们点进源码看返回值
Golang 一日一库之jwt-go
解析方法使用此回调函数提供用于验证的键。函数接收已解析但未验证的令牌。
这允许您使用令牌Header中的属性(例如' kid ')来识别使用哪个键。

上述是源码的意思 而本人理解是制定一个类型规则然后去做解析。不然源码不知道你是制作token 还是解析token

func secret() jwt.Keyfunc {
	//按照这样的规则解析
	return func(t *jwt.Token) (interface{}, error) {
		return []byte("gettoken"), nil
	}
}

解析token

首先需要传入一个token,然后把解析规则传入
然后需要验证Token的正确性以及有效性。
如果二者都是没问题的
然后才能解析出 用户名和密码 或者是其他的一些值

// 解析token
func ParseToken(token string) (user *Userinfo, err error) {
	user = &Userinfo{}
	tokn, _ := jwt.Parse(token, secret())

	claim, ok := tokn.Claims.(jwt.MapClaims)
	if !ok {
		err = errors.New("解析错误")
		return
	}
	if !tokn.Valid {
		err = errors.New("令牌错误!")
		return
	}
	//fmt.Println(claim)
	user.Username = claim["name"].(string) //强行转换为string类型
	user.Password = claim["pwd"].(string)  //强行转换为string类型
	return
}

完整代码

// 本文地址 https://www.cnblogs.com/zichliang/p/17303759.html
package main

import (
	"errors"
	"fmt"
	"github.com/dgrijalva/jwt-go"
	"time"
)

type Userinfo struct {
	Username string `json:"username"`
	Password string `json:"password"`
}

// Macke 生成jwt 需要传入 用户名和密码
func Macke(user *Userinfo) (token string, err error) {
	claims := jwt.MapClaims{ //创建一个自己的声明
		"name": user.Username,
		"pwd":  user.Password,
		"iss":  "lva",
		"nbf":  time.Now().Unix(),
		"exp":  time.Now().Add(time.Second * 4).Unix(),
		"iat":  time.Now().Unix(),
	}

	then := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)

	token, err = then.SignedString([]byte("gettoken"))

	return
}

// secret 自己解析的秘钥
func secret() jwt.Keyfunc {
	//按照这样的规则解析
	return func(t *jwt.Token) (interface{}, error) {
		return []byte("gettoken"), nil
	}
}

// 解析token
func ParseToken(token string) (user *Userinfo, err error) {
	user = &Userinfo{}
	tokn, _ := jwt.Parse(token, secret())

	claim, ok := tokn.Claims.(jwt.MapClaims)
	if !ok {
		err = errors.New("解析错误")
		return
	}
	if !tokn.Valid {
		err = errors.New("令牌错误!")
		return
	}
	//fmt.Println(claim)
	user.Username = claim["name"].(string) //强行转换为string类型
	user.Password = claim["pwd"].(string)  //强行转换为string类型
	return
}

func main() {
	var use = Userinfo{"zic", "admin*123"}
	tkn, _ := Macke(&use)
	fmt.Println("_____", tkn)
	// time.Sleep(time.Second * 8)超过时间打印令牌错误
	user, err := ParseToken(tkn)
	if err != nil {
		fmt.Println(err)
	}
	fmt.Println(user.Username)
}

本文地址 https://www.cnblogs.com/zichliang/p/17303759.html

这里需要注意
用户请求时带上token,服务器解析token后可以获得其中的用户信息,如果token有任何改动,都无法通过验证.文章来源地址https://www.toymoban.com/news/detail-410509.html

到了这里,关于Golang 一日一库之jwt-go的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • golang常用库之-golang-jwt/jwt包

    github: https://github.com/golang-jwt/jwt golang-jwt/jwt 是一个在 Go 语言中使用 JSON Web Tokens(JWT)进行身份验证和授权的库。JWT 是一种用于在跨网络进行安全通信的开放标准(RFC 7519),它使用一个 JSON 对象来表示被声明的安全性信息。 golang-jwt/jwt 包提供了创建、解析和验证 JWT 的功能

    2024年01月23日
    浏览(29)
  • Gin中间件的详解 ,用Jwt-go 和 Gin 的安全的登陆的中间件

    Gin 在不同的group 设置不同的中间件或者过滤器 Gin 的group下的路由上中间件或过滤器 用Jwt-go 和 Gin 的安全的登陆的中间件 JWT 类,它基本有所有基本功能,包括:GenerateToken,GenerateRefreshToken, ValidateToken, ParseToken 1. Gin 在不同的group 设置不同的中间件或者过滤器 Golang 中的 gin

    2024年02月15日
    浏览(49)
  • Golang标准库之bytes介绍

    本次主要介绍golang中的标准库 bytes ,基本上参考了 字节 | bytes 、Golang标准库——bytes 文章。 bytes 库主要包含 5 大部分,即: 常量 变量 函数 Buffer Reader 我们依次学习上面的 5 大部分。 bytes.MinRead 是一个常量,表示在使用 ReadFrom 方法从 io.Reader 中读取数据时,每次读取的最小

    2024年02月07日
    浏览(48)
  • 7天玩转 Golang 标准库之 http/net

    在构建web应用时,我们经常需要处理HTTP请求、做网页抓取或者搭建web服务器等任务,而Go语言在这方面为我们提供了强大的内置工具:net/http标准库,它为我们操作和处理HTTP协议提供了便利。 首先,我们来看看如何使用net/http标准库发送一个HTTP请求。net/http库中的 http.Get 函数

    2024年02月04日
    浏览(40)
  • GOLANG进阶:govalidator过滤器,MD5,JWT身份验证,redis

    引入资源包(两种方式): go get github.com/asaskevich/govalidator go get gopkg.in/asaskevich/govalidator.v10 代码( 可以写在结构体中,也可以单独使用 ): type User struct {     gorm.Model     USERNAME string `json:\\\"name\\\" validate :\\\"min=0,max=35\\\"`     PASSWOR string `validate:\\\"required\\\"`     SEX int8  ` validate :

    2024年02月16日
    浏览(31)
  • 2023软考-系统架构师一日游

    上周六(11月4号)参见了软考,报的系统架构师,今年下半年是第一次推行机考,简单来分享下大致流程,至于考试难度、考点什么的,这个网上有很多专门研究这些的机构,本人无权发言。考试的经过还是可以说一下的,大家可以有个大致的了解。 我是9月25最后一天报的名

    2024年02月05日
    浏览(29)
  • 一日一题:第六题---成绩统计&单词分析

    ​作者:小妮无语 专栏:一日一题 🚶‍♀️✌️道阻且长,不要放弃✌️🏃‍♀️ ​今天主要发现两个很好用的结构,想做个记录 题目描述· 小蓝给学生们组织了一场考试,卷面总分为 100 分,每个学生的得分都是一个 0 到 100 的整数。 如果得分至少是 60 分,则称为及格。

    2023年04月09日
    浏览(33)
  • 每日一库:pprof简介

    pprof 是Go语言的一个性能分析库,它可以帮助开发者找出程序中的性能瓶颈。 pprof 提供了CPU分析、内存分析、阻塞分析等多种性能分析功能。 以下是 pprof 的主要特性: CPU分析 : pprof 可以记录程序在CPU上的运行时间,并将这些数据以火焰图的形式展示出来。这可以帮助开发

    2024年02月10日
    浏览(77)
  • 每日一库:cobra 简介

    当你需要为你的 Go 项目创建一个强大的命令行工具时,你可能会遇到许多挑战,比如如何定义命令、标志和参数,如何生成详细的帮助文档,如何支持子命令等等。为了解决这些问题, github.com/spf13/cobra 就可以派上用场。 github.com/spf13/cobra 是一个用于构建强大的命令行应用程

    2024年02月08日
    浏览(40)
  • 每日一库:fsnotify简介

    fsnotify 是一个用Go编写的文件系统通知库。它提供了一种观察文件系统变化的机制,例如文件的创建、修改、删除、重命名和权限修改。它使用特定平台的事件通知API,例如Linux上的inotify,macOS上的FSEvents,以及Windows上的ReadDirectoryChangesW。 fsnotify 具有以下特点: 跨平台支持

    2024年02月11日
    浏览(31)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包