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

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

何为 jwt token?

什么是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接口 还需要定义几个方法



如上图所示

然后我们使用

使用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强转



像这个样子。

然后我们去执行

传入一个ID 和一个name

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



得到如下值

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJJRCI6MSwiTmFtZSI6IuW8oOS4iSIsIlJlZ2lzdGVyZWRDbGFpbXMiOnsic3ViIjoiVG9rZW4iLCJleHAiOjE2ODExODI2MDYsImlhdCI6MTY4MTE4MjYwNn19.AmOf60S2xby6GmlGgNo4Q5b01cRoAqXWhGorzxbJ2-Q

解析Token

https://jwt.io/

在写代码之前,我们把上面的token丢到上面网站中解析一下



可以发现 有三部分被解析出来了

  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
}

返回成功如下图所示

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

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

结果:

答案是会。

完整代码

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
}

制定解析规则



在自己写的这个函数中 我们点进源码看返回值



解析方法使用此回调函数提供用于验证的键。函数接收已解析但未验证的令牌。

这允许您使用令牌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有任何改动,都无法通过验证.

Golang 常用库之jwt-go的更多相关文章

  1. golang常用库:cli命令行/应用程序生成工具-cobra使用

    golang常用库:cli命令行/应用程序生成工具-cobra使用 一.Cobra 介绍 我前面有一篇文章介绍了配置文件解析库 Viper 的使用,这篇介绍 Cobra 的使用,你猜的没错,这 2 个 ...

  2. golang常用库:配置文件解析库-viper使用

    一.viper简介 viper 配置解析库,是由大神 Steve Francia 开发,他在google领导着 golang 的产品开发,他也是 gohugo.io 的创始人之一,命令行解析库 cob ...

  3. golang常用库:日志记录库-logrus使用

    介绍 logrus 它是一个结构化.插件化的日志记录库.完全兼容 golang 标准库中的日志模块.它还内置了 2 种日志输出格式 JSONFormatter 和 TextFormatter,来定义输 ...

  4. golang常用库:字段参数验证库-validator

    背景 在平常开发中,特别是在web应用开发中,为了验证输入字段的合法性,都会做一些验证操作.比如对用户提交的表单字段进行验证,或者对请求的API接口字段进行验证,验证字段的合法性,保证输入字段值的安全 ...

  5. golang常用库包:Go依赖注入(DI)工具-wire使用

    google 出品的依赖注入库 wire:https://github.com/google/wire 什么是依赖注入 依赖注入 ,英文全名是 dependency injection,简写为 DI. ...

  6. golang 标准库间依赖的可视化展示

    简介 国庆看完 << Go 语言圣经 >>,总想做点什么,来加深下印象.以可视化的方式展示 golang 标准库之间的依赖,可能是一个比较好的切入点.做之前,简单搜了下相关的内 ...

  7. Golang 标准库log的实现

      原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 .作者信息和本声明.否则将追究法律责任.http://gotaly.blog.51cto.com/8861157/1406905 前 ...

  8. 前端Demo常用库文件链接

    <!doctype html><html><head> <meta charset="UTF-8"> <title>前端 ...

  9. GOLANG 常用命令

    golang常用命令: 命令 功能 build      编译包和依赖 run 编译并且直接运行 install 编译安装包和依赖 get 下载并安装包和依赖 fmt 调用gofmt格式化源码文件 d ...

  10. 转:不应该不知道C++的常用库

    不应该不知道C++的常用库 非常惭愧,我过去也仅仅了解boost.STLport这样的库,以及一些GUI库,但是居然有如此众多的C++库,其实令我惊讶.当然,这个问题应该辩证的看,对于拿来主义确实可以 ...

随机推荐

  1. v-show无法隐藏自定义组件

    因为v-show的本质是设置标签display:none,但组件并非是一个标签,所以无法使用v-show控制组件 错误用法: 正确用法:

  2. upload 上传文件

    func SaveUploadedFile(file *multipart.FileHeader, dst string) error{ src, err := file.Open() if err ...

  3. CSS面试题及答案

    介绍一下标准的CSS的盒子模型?与低版本IE的盒子模型有什么不同的? 标准盒子模型:宽度=内容的宽度(content)+ border + padding + margin 低版本IE盒子模型:宽度= ...

  4. NOIP2013普及组

    T1]记数问题 试计算在区间1 到n 的所有整数中,数字x(0 ≤ x ≤ 9)共出现了多少次?例如,在1到11 中,即在1.2.3.4.5.6.7.8.9.10.11中,数字1 出现了4 次. 其实 ...

  5. 探测域名解析依赖关系(运行问题解决No module named 'DNS')

    探测域名解析依赖关系 最近很懒,今天晚上才开始这个任务,然后发现我原来能跑起来的程序跑不起来了. 一直报错 ModuleNotFoundError: No module named 'DNS' 这个应 ...

  6. HOOK大法

    // 请求头hook 测试网站同花顺 OK var header_old = window.XMLHttpRequest.prototype.setRequestHeader;window.XMLHt ...

  7. SAP B1 增加或删除单据,或者是新增修改BOM清单,报错‘出现内部错误时’,一般是控制过程 SBO_SP_TransactionNotification 里面的代码有问题,执行不下去了,因此一行行检查SQL代码,一般就能发现错误所在。

    SAP B1 增加或删除单据,或者是新增修改BOM清单,报错'出现内部错误时',一般是控制过程 SBO_SP_TransactionNotification 里面的代码有问题,执行不下去了,因此一行行 ...

  8. 泛型 ? extends E ? super E的区别

    package cn.itcast.generic; import java.util.ArrayList;import java.util.Collection; public class Supp ...

  9. 西电oj109题处理字符串

    问题描述 从键盘输入一个字符串,将该字符串按下述要求处理后输出: 将ASCII码大于原首字符的各字符按原来相互间的顺序关系集中在原首字符的左边, 将ASCII码小于等于原首字符的各字符按升序集中在原首 ...

  10. Android Studio Dolphin 稳定版正式发布

      作者 / Yuri Blaise, Product Manager, Android为了帮助开发者们更轻松地打造高质量应用,Android Studio 团队深入调研,为大家带来了最新稳定版 An ...