Go-gin-example 第二部分 jwt验证

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

续接上部分

使用 JWT 进行身份校验

在前面几节中,我们已经基本的完成了 API’s 的编写,但是,还存在一些非常严重的问题,例如,我们现在的API是可以随意调用的,这显然还不安全全,在本文中我们通过 jwt-go (GoDoc)的方式来简单解决这个问题。

jwt知识点补充

jwt官网

认识JWT

JSON Web TokenJWT)是一个开放标准(RFC 7519),它定义了一种紧凑和自包含的方式,用于在各方之间作为JSON对象安全地传输信息。

作为标准,它没有提供技术实现,但是大部分的语言平台都有按照它规定的内容提供了自己的技术实现,所以实际在用的时候,只要根据自己当前项目的技术平台,到官网上选用合适的实现库即可。

TOKEN是什么

Token,其实就是服务端生成的一串加密字符串、以作客户端进行请求的一个“令牌”

Go-gin-example 第二部分 jwt验证,golang,gin,网络,数据库,开发语言

jwt的使用场景

以下是JWT两种使用场景:

授权:这是使用 JWT 的最常见的使用场景。用户登录后,每个后续请求都将包含 JWT,允许用户访问使用该令牌允许的路由、服务和资源。单点登录是当今广泛使用 JWT 的一项功能,因为它的开销很小,并且能够跨不同域轻松使用。

信息交换JWT是在各方之间安全传输信息的比较便捷的方式。由于 JWT 可以签名(例如,使用公钥/私钥对),因此可以确定发送者是否是在您的授权范围之内。并且,由于签名是使用标头和有效负载计算的,因此还可以验证内容是否未被篡改

jwt的组成

这是一个JWT的token串:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

其实这一串是经过加密之后的密文字符串,中间通过.来分割。每个.之前的字符串分别表示JWT的三个组成部分:HeaderPayloadSignature

header

Header的主要作用是用来标识,通常是两部分组成

typtype 的简写,令牌类型,也就是JWT

algAlgorithm 的简写,加密签名算法。一般使用HS256jwt官网提供了12种的加密算法

然后通过base64编码,将明文编码,防止在传输过程中能直接一眼看出明文并符合多种传输协议

payload

也称为JWT claims

payload用来承载要传递的数据,它的json结构实际上是对JWT要传递的数据的一组声明,这些声明被JWT标准称为claims,它的一个“属性值对”其实就是一个claim,每一个claim的都代表特定的含义和作用

claims有三类:

  • 保留claims:主要包括iss发行者、exp过期时间、sub主题、aud用户等。
key name 说明
iss 发送者 标识颁发 JWT 的发送主体
sub 主题 标识 JWT 的主题
aud 接收者 标识 JWT 所针对的接收者。每个在处理 JWT 的主体都必须使用受众声明中的值来标识自己。如果处理的主体在存在此声明时未将自己标识为声明中的值,则必须拒绝 JWT
exp 到期时间 标识不得接受 JWT 进行处理的过期时间。该值必须是日期类型,而且是1970-01-01 00:00:00Z 之后的日期秒。
nbf jwt的开始处理的时间 标识 JWT 开始接受处理的时间。该值必须是日期。
iat jwt发出的时间 标识 JWT 的发出的时间。该值必须是日期。
jti jwt id 令牌的区分大小写的唯一标识符,即使在不同的颁发者之间也是如此。

保留claimjwt标准中规定的claim,验证方式已经定义好

  • 公共claims:定义新创的信息,比如用户信息和其他重要信息。(使用较少)
  • 私有claims:用于发布者和消费者都同意以私有的方式使用的信息。

明文实例:

{
  "sub": "12344321",
  "name": "Mars酱", // 私有claims
  "iat": 1516239022
}

base64加密后:

eyJzdWIiOiIxMjM0NDMyMSIsIm5hbWUiOiJNYXJz6YWxIiwiaWF0IjoxNTE2MjM5MDIyfQ
signature

Signature 部分是对HeaderPayload两部分的签名,作用是防止 JWT 被篡改。这个部分的生成规则主要是是公式(伪代码)是:

Header中定义的签名算法alg(
    base64编码(header) + "." + base64编码(payload),
    secret//在服务端加密使用的密钥
)

JWT如果从字面上理解感觉是基于JSON格式用于网络传输令牌。实际上,JWT是一种紧凑的Claims声明格式,,常见的场景如HTTP授权请求头参数和URI查询参数。JWT会把Claims转换成JSON格式,而这个JSON内容将会应用为JWS结构的有效载荷或者应用为JWE结构的(加密处理后的)原始字符串,通过消息认证码(Message Authentication Code或者简称MAC)和/或者加密操作对Claims进行数字签名或者完整性保护。

下载依赖包

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

编写 jwt 工具包

我们需要编写一个jwt的工具包,我们在pkg下的util目录新建jwt.go,写入文件内容:

package util

import (
	"time"

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

	"github.com/kingsill/gin-example/pkg/setting"
)

// 加载配置文件中设置的密钥
var jwtSecret = []byte(setting.JwtSecret)

// Claims 定义claims结构体
type Claims struct {
	Username string `json:"username"`
	Password string `json:"password"`
	jwt.StandardClaims
}

func GenerateToken(username, password string) (string, error) {
	nowTime := time.Now()
	expireTime := nowTime.Add(3 * time.Hour)

	//创建 CustomClaims 结构体,用来封装 jwt 信息
	claims := Claims{
		username,
		password,
		jwt.StandardClaims{
			ExpiresAt: expireTime.Unix(),
			Issuer:    "gin-blog",
		},
	}

	//创建 header和payload部分
	tokenClaims := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)

	//得到完整的token字符串,这里为加入签名signature部分
	token, err := tokenClaims.SignedString(jwtSecret)

	return token, err
}

func ParseToken(token string) (*Claims, error) {
	//解码过程
	tokenClaims, err := jwt.ParseWithClaims(token, &Claims{}, func(token *jwt.Token) (interface{}, error) {
		return jwtSecret, nil
	})

	//验证是否时间过期
	if tokenClaims != nil {
		if claims, ok := tokenClaims.Claims.(*Claims); ok && tokenClaims.Valid {
			return claims, nil
		}
	}

	return nil, err
}

在这个工具包,我们涉及到:

  • NewWithClaims(method SigningMethod, claims Claims)method对应着SigningMethodHMAC struct{},其包含SigningMethodHS256、SigningMethodHS384、SigningMethodHS512三种crypto.Hash方案
  • func (t *Token) SignedString(key interface{}) 该方法内部生成签名字符串,再用于获取完整、已签名的token
  • func (p *Parser) ParseWithClaims 用于解析鉴权的声明,方法内部主要是具体的解码和校验的过程,最终返回*Token
  • func (m MapClaims) Valid() 验证基于时间的声明exp, iat, nbf,注意如果没有任何声明在令牌中,仍然会被认为是有效的。并且对于时区偏差没有计算方法

jwt中间件编写

中间件相关知识
自定义中间件
有了jwt工具包,接下来我们要编写要用于Gin的中间件,我们在middleware下新建jwt目录,新建jwt.go文件,写入内容:

package jwt

import (
	"net/http"
	"time"

	"github.com/gin-gonic/gin"

	"github.com/kingsill/gin-example/pkg/e"
	"github.com/kingsill/gin-example/pkg/util"
)

// 自定义中间件
func JWT() gin.HandlerFunc {
	//返回.context函数
	return func(c *gin.Context) {
		var code int
		var data interface{}

		//默认是正确状态
		code = e.SUCCESS

		//参数查询url中token关键字
		token := c.Query("token")

		//如果为空,则进行相关提示
		if token == "" {
			code = e.INVALID_PARAMS
		} else { //如果右token,进行token的解析
			claims, err := util.ParseToken(token)
			if err != nil { //如果解析出错,相关提示
				code = e.ERROR_AUTH_CHECK_TOKEN_FAIL
			} else if time.Now().Unix() > claims.ExpiresAt { //如果解析出来token已过期,则也有相关提示
				code = e.ERROR_AUTH_CHECK_TOKEN_TIMEOUT
			}
		}

		//后续处理,如果之前步骤有错误,进行一下操作
		if code != e.SUCCESS {
			c.JSON(http.StatusUnauthorized, gin.H{
				"code": code,
				"msg":  e.GetMsg(code),
				"data": data,
			})

			//放弃后续中间件的执行,即如果有错,后续中间件都不执行
			c.Abort()
			return
		}

		//如果没错,放行	next前为请求中间件,next后为相应中间件
		c.Next()
	}
}

如何获取token 编写获取token的Api

那么我们如何调用它呢,我们还要获取Token呢?

models逻辑编写

models下新建auth.go文件,写入内容:

package models

// jwt验证的 数据库相关操作

// Auth 用户对应的struct模型
type Auth struct {
	ID       int    `gorm:"primary_key" json:"id"`
	Username string `json:"username"`
	Password string `json:"password"`
}

// CheckAuth 根据用户名和密码查询用户是否存在
func CheckAuth(username, password string) bool {
	var auth Auth
	db.Select("id").Where(Auth{Username: username, Password: password}).First(&auth)
	if auth.ID > 0 {
		return true
	}

	return false
}

}

路由逻辑编写

routers下的api目录新建auth.go文件,写入内容:

package v1

import (
	"log"
	"net/http"

	"github.com/astaxie/beego/validation"
	"github.com/gin-gonic/gin"

	"github.com/kingsill/gin-example/models"
	"github.com/kingsill/gin-example/pkg/e"
	"github.com/kingsill/gin-example/pkg/util"
)

// 定义我们验证用户所需的信息,同时定义valid验证的预定信息,即一定要有并且最大字符数为50
type auth struct {
	Username string `valid:"Required; MaxSize(50)"`
	Password string `valid:"Required; MaxSize(50)"`
}

func GetAuth(c *gin.Context) {
	//参数查询,?=模式,获取用户名和密码
	username := c.Query("username")
	password := c.Query("password")

	//通过设立结构体验证的valid验证,即所需并且最大为50个字符
	valid := validation.Validation{}
	a := auth{Username: username, Password: password}
	ok, _ := valid.Valid(&a)

	//建立存储信息的map key类型为string,val类型为任意值
	data := make(map[string]interface{})

	//设置默认code为参数错误
	code := e.INVALID_PARAMS

	//通过前序验证,继续通过数据库进行验证
	if ok {
		//通过数据库进行验证
		isExist := models.CheckAuth(username, password)

		//如果通过数据库验证
		if isExist {
			//创建token令牌
			token, err := util.GenerateToken(username, password)

			if err != nil { //如果生成令牌失败
				code = e.ERROR_AUTH_TOKEN
			} else { //生成token成功,进行存储
				data["token"] = token
				code = e.SUCCESS
			}

		} else { //没通过数据库验证
			code = e.ERROR_AUTH
		}

	} else { //没通过前序验证
		for _, err := range valid.Errors {
			log.Println(err.Key, err.Message)
		}
	}

	//json相应
	c.JSON(http.StatusOK, gin.H{
		"code": code,
		"msg":  e.GetMsg(code),
		"data": data,
	})
}

修改路由逻辑

我们打开routers目录下的router.go文件,修改文件内容(新增获取 token 的方法):
增添一句 r.GET("/auth", api.GetAuth),放在路由组之外

func InitRouter() *gin.Engine {
r := gin.New()

r.Use(gin.Logger())

r.Use(gin.Recovery())

gin.SetMode(setting.RunMode)

r.GET("/auth", api.GetAuth)//------------------------

apiv1 := r.Group("/api/v1")
{
...
}

return r
}

验证token

获取token API 方法就到这里啦,让我们来测试下是否可以正常使用吧!

重启服务后,用GET方式访问http://127.0.0.1:8000/auth?username=test&password=test123456 ,查看返回值是否正确

{
    "code": 200,
    "data": {
        "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6InRlc3QiLCJwYXNzd29yZCI6InRlc3QxMjM0NTYiLCJleHAiOjE3MDUzMzk2MzUsImlzcyI6Imdpbi1ibG9nIn0.4zblfic9MdOvrg4TF9Li8nfw3FSBq3rGgKqnJnDFXYY"
    },
    "msg": "ok"
}

我们有了tokenAPI,也调用成功了

将中间件接入Gin

修改路由分组模块语句,apiv1 := r.Group("/api/v1").Use(jwt.JWT()),将jwt验证加入全局路由

...
    apiv1 := r.Group("/api/v1")
apiv1.Use(jwt.JWT())
{
...
}
...

当前目录结构

go-gin-example/
├── conf
│   └── app.ini
├── main.go
├── middleware
│   └── jwt
│       └── jwt.go
├── models
│   ├── article.go
│   ├── auth.go
│   ├── models.go
│   └── tag.go
├── pkg
│   ├── e
│   │   ├── code.go
│   │   └── msg.go
│   ├── setting
│   │   └── setting.go
│   └── util
│       ├── jwt.go
│       └── pagination.go
├── routers
│   ├── api
│   │   ├── auth.go
│   │   └── v1
│   │       ├── article.go
│   │       └── tag.go
│   └── router.go
├── runtime

到这里,我们的JWT编写就完成啦!

功能验证模块

我们来测试一下,再次访问

  • http://127.0.0.1:8000/api/v1/articles
  • http://127.0.0.1:8000/api/v1/articles?token=23131
    正确的反馈应该是
{
  "code": 400,
  "data": null,
  "msg": "请求参数错误"
}

{
  "code": 20001,
  "data": null,
  "msg": "Token鉴权失败"
}

我们需要访问http://127.0.0.1:8000/auth?username=test&password=test123456 ,得到token

再用包含tokenURL 参数去访问我们的应用 API

  • 这里的问题即为创建的token还需要自己复制粘贴,不能自动取用等

访问http://127.0.0.1:8000/api/v1/articles?token=eyJhbGci... ,检查接口返回值

{
    "code": 200,
    "data": {
        "lists": [],
        "total": 0
    },
    "msg": "ok"
}

验证正确,文章列表取决于数据库内容文章来源地址https://www.toymoban.com/news/detail-797066.html

到了这里,关于Go-gin-example 第二部分 jwt验证的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

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

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

相关文章

  • Golang中Gin 参数绑定和验证的中间件

    1. 学习在Golang中使用Gin参数绑定和验证的中间件,了解不同参数类型的绑定和验证方式。 Gin框架提供了很多常用的中间件,其中就包括参数绑定和验证的中间件。在使用Gin框架中进行数据绑定和验证时,可以使用Gin内置的Binding、Validating和Uri中间件。 1. Binding Binding中间件用于

    2024年02月08日
    浏览(49)
  • go gin 自定义验证

    我们上一篇已经提到了gin中binding时候可以指定json字段大小等限制,但是那个错误却是英文的,现在想搞成中文的,以便前端可读,demo如下

    2024年02月10日
    浏览(37)
  • [golang gin框架] 40.Gin商城项目-微服务实战之Captcha验证码微服务

    本次内容需要 gin框架基础知识, golang微服务基础知识才能更好理解 在前面,讲解了微服务的架构等,这里,来讲解前面商城项目的 Captcha验证码 微服务 ,captcha验证码功能在前台,后端 都要用到 ,可以把它 抽离出来 ,做成微服务功能 编辑 这个验证码功能封装代码captcha.go如下: 把这个

    2024年02月16日
    浏览(44)
  • go gin 参数绑定常用验证器

    https://pkg.go.dev/github.com/go-playground/validator/v10#readme-baked-in-validations min 最小 max 最大 len 长度限制 gt 大于 eq 等于 ne 不等于 eqfield 与某个字段值一样 nefield 与某个字段值不一样 oneof 枚举 ,以空格分开 startswith endswith dive 数组

    2024年02月11日
    浏览(39)
  • Golang 一日一库之jwt-go

    本文地址 https://www.cnblogs.com/zichliang/p/17303759.html github地址:https://github.com/dgrijalva/jwt-go 什么是JSON Web Token? JSON Web Token(JWT)是一个开放标准(RFC 7519),它定义了一种紧凑且自包含的方式,用于在各方之间以JSON方式安全地传输信息。由于此信息是经过数字签名的,因此可以

    2023年04月11日
    浏览(49)
  • 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日
    浏览(43)
  • Go学习第十五章——Gin参数绑定bind与验证器

    在Gin框架中, bind 用于绑定参数,即将请求参数绑定到结构体中。通过使用 bind ,我们可以方便地将请求参数与结构体字段进行绑定,从而更方便地处理和验证参数。 Gin框架提供了多种绑定方法,包括Query参数绑定、Form参数绑定、JSON参数绑定等。下面分别介绍这些方法的详

    2024年02月07日
    浏览(42)
  • 【Golang星辰图】加密和安全进阶:拓展Go的加密能力与身份验证

    在当今信息时代,保护用户数据和网络通信的安全至关重要。为了确保应用程序和用户之间的数据传输的机密性、完整性和真实性,加密和安全技术成为开发者必须掌握的关键领域之一。Go语言作为一种强大而灵活的编程语言,提供了丰富的加密和安全功能。本文将介绍一些

    2024年04月11日
    浏览(62)
  • gin中使用JWT

    安装 默认Claim 如果我们直接使用JWT中默认的字段,没有其他定制化的需求则可以直接使用这个包中的和方法快速生成和解析token。 自定义Claims 我们需要定制自己的需求来决定JWT中保存哪些数据,比如我们规定在JWT中要存储 username 信息,那么我们就定义一个 MyClaims 结构体如下

    2024年01月21日
    浏览(38)
  • Go语言网络编程入门:TCP、HTTP、JSON序列化、Gin、WebSocket、RPC、gRPC示例

    在本文中,我们将介绍Go语言中的网络编程的不同方式,包括TCP、HTTP、Gin框架、WebSocket、RPC、gRPC的介绍与连接实例,并对所有示例代码都给出了详细的注释,最后对每种模式进行了总结。 TCP(传输控制协议)是一种面向连接的、可靠的、基于字节流的传输层通信协议,提供

    2024年02月16日
    浏览(59)

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

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

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

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

二维码1

领取红包

二维码2

领红包