在gin框架中使用JWT
在gin框架中使用JWT
JWT全称JSON Web Token是一种跨域认证解决方案,属于一个开放的标准,它规定了一种Token实现方式,目前多用于前后端分离项目和OAuth2.0业务场景下。
什么是JWT?
JWT全称JSON Web Token是一种跨域认证解决方案,属于一个开放的标准,它规定了一种Token实现方式,目前多用于前后端分离项目和OAuth2.0业务场景下。
为什么需要JWT?
在之前的一些web项目中,我们通常使用的是Cookie-Session模式实现用户认证。相关流程大致如下:
- 用户在浏览器端填写用户名和密码,并发送给服务端
- 服务端对用户名和密码校验通过后会生成一份保存当前用户相关信息的session数据和一个与之对应的标识(通常称为session_id)
- 服务端返回响应时将上一步的session_id写入用户浏览器的Cookie
- 后续用户来自该浏览器的每次请求都会自动携带包含session_id的Cookie
- 服务端通过请求中的session_id就能找到之前保存的该用户那份session数据,从而获取该用户的相关信息。
这种方案依赖于客户端(浏览器)保存Cookie,并且需要在服务端存储用户的session数据。
在移动互联网时代,我们的用户可能使用浏览器也可能使用APP来访问我们的服务,我们的web应用可能是前后端分开部署在不同的端口,有时候我们还需要支持第三方登录,这下Cookie-Session的模式就有些力不从心了。
JWT就是一种基于Token的轻量级认证模式,服务端认证通过后,会生成一个JSON对象,经过签名后得到一个Token(令牌)再发回给用户,用户后续请求只需要带上这个Token,服务端解密之后就能获取该用户的相关信息了。
想要连接JWT的原理,推荐大家阅读:阮一峰的JWT入门教程
生成JWT和解析JWT
我们在这里直接使用jwt-go这个库来实现我们生成JWT和解析JWT的功能。
定义需求
我们需要定制自己的需求来决定JWT中保存哪些数据,比如我们规定在JWT中要存储username信息,那么我们就定义一个MyClaims结构体如下:
// MyClaims 自定义声明结构体并内嵌jwt.StandardClaims
// jwt包自带的jwt.StandardClaims只包含了官方字段
// 我们这里需要额外记录一个username字段,所以要自定义结构体
// 如果想要保存更多信息,都可以添加到这个结构体中
type MyClaims struct {
Username string `json:"username"`
jwt.StandardClaims
}
然后我们定义JWT的过期时间,这里以2小时为例:
const TokenExpireDuration = time.Hour * 2
接下来还需要定义Secret:
var MySecret = []byte("夏天夏天悄悄过去")
生成JWT
// GenToken 生成JWT
func GenToken(username string) (string, error) {
// 创建一个我们自己的声明
c := MyClaims{
"username", // 自定义字段
jwt.StandardClaims{
ExpiresAt: time.Now().Add(TokenExpireDuration).Unix(), // 过期时间
Issuer: "my-project", // 签发人
},
}
// 使用指定的签名方法创建签名对象
token := jwt.NewWithClaims(jwt.SigningMethodHS256, c)
// 使用指定的secret签名并获得完整的编码后的字符串token
return token.SignedString(MySecret)
}
解析JWT
// ParseToken 解析JWT
func ParseToken(tokenString string) (*MyClaims, error) {
// 解析token
token, err := jwt.ParseWithClaims(tokenString, &MyClaims{}, func(token *jwt.Token) (i interface{}, err error) {
return MySecret, nil
})
if err != nil {
return nil, err
}
if claims, ok := token.Claims.(*MyClaims); ok && token.Valid { // 校验token
return claims, nil
}
return nil, errors.New("invalid token")
}
在gin框架中使用JWT
首先我们注册一条路由/auth,对外提供获取Token的渠道:
r.POST("/auth", authHandler)
我们的authHandler定义如下:
func authHandler(c *gin.Context) {
// 用户发送用户名和密码过来
var user UserInfo
err := c.ShouldBind(&user)
if err != nil {
c.JSON(http.StatusOK, gin.H{
"code": 2001,
"msg": "无效的参数",
})
return
}
// 校验用户名和密码是否正确
if user.Username == "q1mi" && user.Password == "q1mi123" {
// 生成Token
tokenString, _ := GenToken(user.Username)
c.JSON(http.StatusOK, gin.H{
"code": 2000,
"msg": "success",
"data": gin.H{"token": tokenString},
})
return
}
c.JSON(http.StatusOK, gin.H{
"code": 2002,
"msg": "鉴权失败",
})
return
}
用户通过上面的接口获取Token之后,后续就会携带着Token再来请求我们的其他接口,这个时候就需要对这些请求的Token进行校验操作了,很显然我们应该实现一个检验Token的中间件,具体实现如下:
// JWTAuthMiddleware 基于JWT的认证中间件
func JWTAuthMiddleware() func(c *gin.Context) {
return func(c *gin.Context) {
// 客户端携带Token有三种方式 1.放在请求头 2.放在请求体 3.放在URI
// 这里假设Token放在Header的Authorization中,并使用Bearer开头
// 这里的具体实现方式要依据你的实际业务情况决定
authHeader := c.Request.Header.Get("Authorization")
if authHeader == "" {
c.JSON(http.StatusOK, gin.H{
"code": 2003,
"msg": "请求头中auth为空",
})
c.Abort()
return
}
// 按空格分割
parts := strings.SplitN(authHeader, " ", 2)
if !(len(parts) == 2 && parts[0] == "Bearer") {
c.JSON(http.StatusOK, gin.H{
"code": 2004,
"msg": "请求头中auth格式有误",
})
c.Abort()
return
}
// parts[1]是获取到的tokenString,我们使用之前定义好的解析JWT的函数来解析它
mc, err := ParseToken(parts[1])
if err != nil {
c.JSON(http.StatusOK, gin.H{
"code": 2005,
"msg": "无效的Token",
})
c.Abort()
return
}
// 将当前请求的username信息保存到请求的上下文c上
c.Set("username", mc.Username)
c.Next() // 后续的处理函数可以用过c.Get("username")来获取当前请求的用户信息
}
}
注册一个/home路由,发个请求验证一下吧。
r.GET("/home", JWTAuthMiddleware(), homeHandler)
func homeHandler(c *gin.Context) {
username := c.MustGet("username").(string)
c.JSON(http.StatusOK, gin.H{
"code": 2000,
"msg": "success",
"data": gin.H{"username": username},
})
}
如果不想自己实现上述功能,你也可以使用Github上别人封装好的包,比如https://github.com/appleboy/gin-jwt。
在gin框架中使用JWT的更多相关文章
- gin框架中使用jwt
生成解析token 如今有很多将身份验证内置到API中的方法 -JSON Web令牌只是其中之一.JSON Web令牌(JWT)作为令牌系统而不是在每次请求时都发送用户名和密码,因此比其他方法(如基本 ...
- golang gin框架中实现一个简单的不是特别精确的秒级限流器
起因 看了两篇关于golang中限流器的帖子: Gin 开发实践:如何实现限流中间件 常用限流策略--漏桶与令牌桶介绍 我照着用,居然没效果-- 时间有限没有深究.这实在是一个很简单的功能,我的需求是 ...
- gin框架中请求路由组的使用
1. gin框架中可以使用路由组来实现对路由的分类 package main import "github.com/gin-gonic/gin" func main() { rou ...
- gin框架中的路由
基本路由 gin框架中采用的路由库是基于httrouter做的 地址为:https://github.com/julienschmidt/httprouter httprouter路由库 点击查看代码 ...
- 【解决了一个小问题】gin框架中出现如下错误:"[GIN-debug] [WARNING] Headers were already written. Wanted to override status code 400 with 500"
POST到数据到一条gin框架的接口后,客户端收到400错误,并且返回了业务中返回的"decode json fail". 关键代码是: func report(c *gin.Co ...
- golang gin框架中实现大文件的流式上传
一般来说,通过c.Request.FormFile()获取文件的时候,所有内容都全部读到了内存.如果是个巨大的文件,则可能内存会爆掉:且,有的时候我们需要一边上传一边处理. 以下的代码实现了大文件流式 ...
- gin框架中中间件的编写与使用
概念 一个完整的系统可能包括鉴权认证.权限管理.安全检查.日志记录等多维度的系统支持. 中间件位与服务器和实际业务处理程序之间,其含义就相当于在请求和具体的业务处理逻辑之间增加某些操作,这种以额外增加 ...
- gin框架中的参数验证
结构体验证 用gin框架的数据验证,可以不用解析数据,减少if else,会简洁许多. 处理请求方法 func structValidator(context *gin.Context) { var ...
- gin框架中请求参数的绑定与多数据格式处理
package main import ( "fmt" "github.com/gin-gonic/gin" ) // gin框架提供给开发者表单实体绑定的功能 ...
随机推荐
- helm安装及使用
helm简介 官网文档:https://helm.sh/ helm是kubernetes的包管理器,类似于linux系统下的apt-get或yum 安装 wget https://get.helm.s ...
- new word
strategy: a plan of action or policy designed to achieve a major or overall aim.
- c协程库libco几点体会
https://www.cnblogs.com/dearplain/p/9820913.html 这里说的是Tencent开源的libco. libco的用途和依赖 主要还是c/c++服务端,相比li ...
- 01- Java概述
一 Java简介 java语言发展史 发展史简单了解:如下: https://www.jianshu.com/p/a78fcb3ccf63 java语言平台 JavaSE(标准版):可以用户开发普通桌 ...
- 数据分析处理之PCA OLSR PCR PLSR(NIPALS)及其Matlab代码实现
传统的OLS(普通最小二乘)方法无法解决样本数据的共线性(multicollinearity)问题,如果你的数据样本中每个特征变量具有共线性,那么使用基于PCA的PCR和PLSR方法对数据样本进行回归 ...
- 详谈lastIndex对正则结果的影响
前言 今天遇到一个问题,用正则表达式去检查同一个字符串时,交替返回true和false.无奈之下,重新翻了翻权威指南,发现罪魁祸首原来是lastIndex.可在控制台尝试下 ? 1 2 3 4 5 6 ...
- hdu5025 状态压缩广搜
题意: 悟空要救唐僧,中途有最多就把钥匙,和最多五条蛇,要求就得唐僧并且拿到所有种类的钥匙(两个1只拿一个就行),拿钥匙i之前必须拿到钥匙i-1,打蛇多花费一秒,问救出唐僧并且拿到所有种类 ...
- Win64 驱动内核编程-8.内核里的其他常用
内核里的其他常用 1.遍历链表.内核里有很多数据结构,但它们并不是孤立的,内核使用双向链表把它们像糖 葫芦一样给串了起来.所以遍历双向链表能获得很多重要的内核数据.举个简单的例子,驱 动对象 Driv ...
- Redis数据结构—链表与字典的结构
目录 Redis数据结构-链表与字典的结构 链表 Redis链表节点的结构 Redis链表的表示 Redis链表用在哪 字典 Redis字典结构总览 Redis字典结构分解 Redis字典的使用 Re ...
- 这次我好像才真的明白了CSS Rem字体计算的原理
背景 如何按照设计稿中标注的尺寸,直接写页面的样式,不再需要px2rem这样的工具或者人工转换 ? 只要你明白了rem的计算原理,这个问题的答案超级简单. 根字体大小计算核心原理 设备的根字体大小 * ...