当前位置: 代码网 > it编程>前端脚本>Golang > Golang 在gin框架中如何使用JWT鉴权

Golang 在gin框架中如何使用JWT鉴权

2024年09月09日 Golang 我要评论
什么是jwtjwt,全称 json web token,是一种开放标准(rfc 7519),用于安全地在双方之间传递信息。尤其适用于身份验证和授权场景。jwt 的设计允许信息在各方之间安全地、 com

什么是jwt

jwt,全称 json web token,是一种开放标准(rfc 7519),用于安全地在双方之间传递信息。尤其适用于身份验证和授权场景。jwt 的设计允许信息在各方之间安全地、 compactly(紧凑地)传输,因为其自身包含了所有需要的认证信息,从而减少了需要查询数据库或会话存储的需求。

jwt主要由三部分组成,通过.连接:

  • header(头部):描述jwt的元数据,通常包括类型(通常是jwt)和使用的签名算法(如hs256rs256等)。
  • payload(载荷):包含声明(claims),即用户的相关信息。这些信息可以是公开的,也可以是私有的,但应避免放入敏感信息,因为该部分可以被解码查看。载荷中的声明可以验证,但不加密。
  • signature(签名):用于验证jwt的完整性和来源。它是通过将header和payload分别进行base64编码后,再与一个秘钥(secret)一起通过指定的算法(如hmac sha256)计算得出的。

jwt的工作流程大致如下:

  • 认证阶段:用户向服务器提供凭证(如用户名和密码)。服务器验证凭证无误后,生成一个jwt,其中包含用户标识符和其他声明,并使用秘钥对其进行签名。
  • 使用阶段:客户端收到jwt后,可以在后续的每个请求中将其放在http请求头中发送给服务器,以此证明自己的身份。
  • 验证阶段:服务器收到jwt后,会使用相同的秘钥验证jwt的签名,确保其未被篡改,并检查过期时间等其他声明,从而决定是否允许执行请求。

jwt的优势在于它的无状态性,服务器不需要存储会话信息,这减轻了服务器的压力,同时也方便了跨域认证。但需要注意的是,jwt的安全性依赖于秘钥的安全保管以及对jwt过期时间等的合理设置。

api设计

这里设计两个公共接口和一个受保护的接口。

api描述
/api/login公开接口。用于用户登录
/api/register公开接口。用于用户注册
/api/admin/user保护接口,需要验证jwt

开发准备

初始化项目目录并切换进入

mkdir gin-jwt
cd gin-jwt

使用go mod初始化工程

go mod init gin-jwt

安装依赖

go get -u github.com/gin-gonic/gin
go get -u gorm.io/gorm
go get -u gorm.io/driver/postgres
go get -u github.com/golang-jwt/jwt/v5
go get -u github.com/joho/godotenv
go get -u golang.org/x/crypto

创建第一个api

一开始我们可以在项目的根目录中创建文件main.go

touch main.go

添加以下内容

package main
import (
	"net/http"
	"github.com/gin-gonic/gin"
)
func main() {
	r := gin.default()
	public := r.group("/api")
	{
		public.post("/register", func(c *gin.context) {
			c.json(http.statusok, gin.h{
				"data": "test. register api",
			})
		})
	}
	r.run("0.0.0.0:8000")
}

测试运行

go run main.go

客户端测试。正常的话会有以下输出

$ curl -x post http://127.0.0.1:8000/api/register
{"data":"test. register api"}

完善register接口

现在register接口已经准备好了,但一般来说我们会把接口业务逻辑放在单独的文件中,而不是和接口定义写在一块。

创建一个控制器的包目录,并添加文件

mkdir controllers
touch controllers/auth.go

auth.go文件内容

package controllers
import (
	"net/http"
	"github.com/gin-gonic/gin"
)
func register(c *gin.context) {
	c.json(http.statusok, gin.h{
		"data": "hello, this is register endpoint",
	})
}

更新main.go文件

package main
import (
	"github.com/gin-gonic/gin"
	"gin-jwt/controllers"
)
func main() {
	r := gin.default()
	public := r.group("/api")
	{
		public.post("/register", controllers.register)
	}
	r.run("0.0.0.0:8000")
}

重新运行测试

go run main.go

客户端测试

$ curl -x post http://127.0.0.1:8000/api/register
{"data":"hello, this is register endpoint"}

解析register的客户端请求

客户端请求register api需要携带用户名和密码的参数,服务端对此做解析。编辑文件controllers/auth.go

package controllers
import (
	"net/http"
	"github.com/gin-gonic/gin"
)
// /api/register的请求体
type reqregister struct {
	username string `json:"username" binding:"required"`
	password string `json:"password" binding:"required"`
}
func register(c *gin.context) {
	var req reqregister
	if err := c.shouldbindbodywithjson(&req); err != nil {
		c.json(http.statusbadrequest, gin.h{
			"data": err.error(),
		})
		return
	}
	c.json(http.statusok, gin.h{
		"data": req,
	})
}

客户端请求测试

$ curl -x post http://127.0.0.1:8000/api/register -d '{"username": "zhangsan", "password": "123456"}' -h 'content-type=application/json'
{"data":{"username":"zhangsan","password":"123456"}}

连接关系型数据库

一般会将数据保存到专门的数据库中,这里用postgresql来存储数据。postgres使用docker来安装。安装完postgres后,创建用户和数据库:

create user ginjwt encrypted password 'ginjwt';
create database ginjwt owner = ginjwt;

创建目录models,这个目录将包含连接数据库和数据模型的代码。

mkdir models

编辑文件models/setup.go

package models
import (
	"fmt"
	"log"
	"os"
	"github.com/joho/godotenv"
	"gorm.io/driver/postgres"
	"gorm.io/gorm"
)
var db *gorm.db
func connectdatabase() {
	err := godotenv.load(".env")
	if err != nil {
		log.fatalf("error loading .env file. %v\n", err)
	}
	// dbdriver := os.getenv("db_driver")
	dbhost := os.getenv("db_host")
	dbport := os.getenv("db_port")
	dbuser := os.getenv("db_user")
	dbpass := os.getenv("db_pass")
	dbname := os.getenv("db_name")
	dsn := fmt.sprintf("host=%s port=%s user=%s dbname=%s sslmode=disable timezone=asia/shanghai password=%s", dbhost, dbport, dbuser, dbname, dbpass)
	db, err = gorm.open(postgres.open(dsn), &gorm.config{})
	if err != nil {
		log.fatalf("connect to database failed, %v\n", err)
	} else {
		log.printf("connect to database success, host: %s, port: %s, user: %s, dbname: %s\n", dbhost, dbport, dbuser, dbname)
	}
	// 迁移数据表
	db.automigrate(&user{})
}

新建并编辑环境配置文件.env

db_host=127.0.0.1
db_port=5432
db_user=ginjwt
db_pass=ginjwt
db_name=ginjwt

创建用户模型,编辑代码文件models/user.go

package models
import (
	"html"
	"strings"
	"golang.org/x/crypto/bcrypt"
	"gorm.io/gorm"
)
type user struct {
	gorm.model
	username string `gorm:"size:255;not null;unique" json:"username"`
	password string `gorm:"size:255;not null;" json:"password"`
}
func (u *user) saveuser() (*user, error) {
	err := db.create(&u).error
	if err != nil {
		return &user{}, err
	}
	return u, nil
}
// 使用gorm的hook在保存密码前对密码进行hash
func (u *user) beforesave(tx *gorm.db) error {
	hashedpassword, err := bcrypt.generatefrompassword([]byte(u.password), bcrypt.defaultcost)
	if err != nil {
		return err
	}
	u.password = string(hashedpassword)
	u.username = html.escapestring(strings.trimspace(u.username))
	return nil
}

更新main.go

package main
import (
	"github.com/gin-gonic/gin"
	"gin-jwt/controllers"
	"gin-jwt/models"
)
func init() {
	models.connectdatabase()
}
func main() {
	r := gin.default()
	public := r.group("/api")
	{
		public.post("/register", controllers.register)
	}
	r.run("0.0.0.0:8000")
}

更新controllers/auth.go

package controllers
import (
	"net/http"
	"gin-jwt/models"
	"github.com/gin-gonic/gin"
)
// /api/register的请求体
type reqregister struct {
	username string `json:"username" binding:"required"`
	password string `json:"password" binding:"required"`
}
func register(c *gin.context) {
	var req reqregister
	if err := c.shouldbindbodywithjson(&req); err != nil {
		c.json(http.statusbadrequest, gin.h{
			"data": err.error(),
		})
		return
	}
	u := models.user{
		username: req.username,
		password: req.password,
	}
	_, err := u.saveuser()
	if err != nil {
		c.json(http.statusbadrequest, gin.h{
			"data": err.error(),
		})
		return
	}
	c.json(http.statusok, gin.h{
		"message": "register success",
		"data":    req,
	})
}

重新运行服务端后,客户端测试

$ curl -x post http://127.0.0.1:8000/api/register -d '{"username": "zhangsan", "password": "123456"}' -h 'content-type=application/json'
{"data":{"username":"zhangsan","password":"123456"},"message":"register success"}

添加login接口

登录接口实现的也非常简单,只需要提供用户名和密码参数。服务端接收到客户端的请求后到数据库中去匹配,确认用户是否存在和密码是否正确。如果验证通过则返回一个token,否则返回异常响应。

首先在main.go中注册api

// xxx
func main() {
	// xxx
	r := gin.default()
	public := r.group("/api")
	{
		public.post("/register", controllers.register)
		public.post("/login", controllers.login)
	}
}

auth.go中添加login控制器函数

// api/login 的请求体
type reqlogin struct {
	username string `json:"username" binding:"required"`
	password string `json:"password" binding:"required"`
}
func login(c *gin.context) {
	var req reqlogin
	if err := c.shouldbindbodywithjson(&req); err != nil {
		c.json(http.statusbadrequest, gin.h{"error": err.error()})
		return
	}
	u := models.user{
		username: req.username,
		password: req.password,
	}
	// 调用 models.logincheck 对用户名和密码进行验证
	token, err := models.logincheck(u.username, u.password)
	if err != nil {
		c.json(http.statusbadrequest, gin.h{
			"error": "username or password is incorrect.",
		})
		return
	}
	c.json(http.statusok, gin.h{
		"token": token,
	})
}

logincheck方法在models/user.go文件中实现

package models
import (
	"gin-jwt/utils/token"
	"html"
	"strings"
	"golang.org/x/crypto/bcrypt"
	"gorm.io/gorm"
)
func verifypassword(password, hashedpassword string) error {
	return bcrypt.comparehashandpassword([]byte(hashedpassword), []byte(password))
}
func logincheck(username, password string) (string, error) {
	var err error
	u := user{}
	err = db.model(user{}).where("username = ?", username).take(&u).error
	if err != nil {
		return "", err
	}
	err = verifypassword(password, u.password)
	if err != nil && err == bcrypt.errmismatchedhashandpassword {
		return "", err
	}
	token, err := token.generatetoken(u.id)
	if err != nil {
		return "", err
	}
	return token, nil
}

这里将token相关的函数放到了单独的模块中,新增相关目录并编辑文件

mkdir -p utils/token
touch utils/token/token.go

以下代码为token.go的内容,包含的几个函数在后面会用到

package token
import (
	"fmt"
	"os"
	"strconv"
	"strings"
	"time"
	"github.com/gin-gonic/gin"
	"github.com/golang-jwt/jwt/v5"
)
func generatetoken(user_id uint) (string, error) {
	token_lifespan, err := strconv.atoi(os.getenv("token_hour_lifespan"))
	if err != nil {
		return "", err
	}
	claims := jwt.mapclaims{}
	claims["authorized"] = true
	claims["user_id"] = user_id
	claims["exp"] = time.now().add(time.hour * time.duration(token_lifespan)).unix()
	token := jwt.newwithclaims(jwt.signingmethodhs256, claims)
	return token.signedstring([]byte(os.getenv("api_secret")))
}
func tokenvalid(c *gin.context) error {
	tokenstring := extracttoken(c)
	fmt.println(tokenstring)
	_, err := jwt.parse(tokenstring, func(token *jwt.token) (any, error) {
		if _, ok := token.method.(*jwt.signingmethodhmac); !ok {
			return nil, fmt.errorf("unexpected signing method: %v", token.header["alg"])
		}
		return []byte(os.getenv("api_secret")), nil
	})
	if err != nil {
		return err
	}
	return nil
}
// 从请求头中获取token
func extracttoken(c *gin.context) string {
	bearertoken := c.getheader("authorization")
	if len(strings.split(bearertoken, " ")) == 2 {
		return strings.split(bearertoken, " ")[1]
	}
	return ""
}
// 从jwt中解析出user_id
func extracttokenid(c *gin.context) (uint, error) {
	tokenstring := extracttoken(c)
	token, err := jwt.parse(tokenstring, func(token *jwt.token) (any, error) {
		if _, ok := token.method.(*jwt.signingmethodhmac); !ok {
			return nil, fmt.errorf("unexpected signing method: %v", token.header["alg"])
		}
		return []byte(os.getenv("api_secret")), nil
	})
	if err != nil {
		return 0, err
	}
	claims, ok := token.claims.(jwt.mapclaims)
	// 如果jwt有效,将user_id转换为浮点数字符串,然后再转换为 uint32
	if ok && token.valid {
		uid, err := strconv.parseuint(fmt.sprintf("%.0f", claims["user_id"]), 10, 32)
		if err != nil {
			return 0, err
		}
		return uint(uid), nil
	}
	return 0, nil
}

.env文件中添加两个环境变量的配置。token_hour_lifespan设置token的过期时长,api_secret是jwt的密钥。

token_hour_lifespan=1
api_secret="wp3-sn6&gg4-lv8>gj9)"

测试,这里改用python代码进行测试

import requests
import json
headers = {
    "content-type": "application/json",
}
resp = requests.get("http://127.0.0.1:8000/api/admin/user", headers=headers)
def register(username: str, password: str):
    req_body = {
        "username": username,
        "password": password,
    }
    resp = requests.post("http://127.0.0.1:8000/api/register", data=json.dumps(req_body), headers=headers)
    print(resp.text)
def login(username: str, password: str):
    req_body = {
        "username": username,
        "password": password,
    }
    resp = requests.post("http://127.0.0.1:8000/api/login", data=json.dumps(req_body), headers=headers)
    print(resp.text)
    if resp.status_code == 200:
        return resp.json()["token"]
    else:
        return ""
if __name__ == "__main__":
    username = "lisi"
    password = "123456"
    register(username, password)
    token = login(username, password)
	print(token)

创建jwt认证中间件

创建中间件目录和代码文件

mkdir middlewares
touch middlewares/middlewares.go

内容如下

package middlewares
import (
	"gin-jwt/utils/token"
	"net/http"
	"github.com/gin-gonic/gin"
)
func jwtauthmiddleware() gin.handlerfunc {
	return func(c *gin.context) {
		err := token.tokenvalid(c)
		if err != nil {
			c.string(http.statusunauthorized, err.error())
			c.abort()
			return
		}
		c.next()
	}
}

main.go文件中注册路由的时候使用中间件

func main() {
	models.connectdatabase()
	r := gin.default()
	public := r.group("/api")
	{
		public.post("/register", controllers.register)
		public.post("/login", controllers.login)
	}
	protected := r.group("/api/admin")
	{
		protected.use(middlewares.jwtauthmiddleware())
		protected.get("/user", func(c *gin.context) {
			c.json(http.statusok, gin.h{
				"status":  "success",
				"message": "authorized",
			})
		})
	}
	r.run("0.0.0.0:8000")
}

controllers/auth.go文件中实现currentuser

func currentuser(c *gin.context) {
	// 从token中解析出user_id
	user_id, err := token.extracttokenid(c)
	if err != nil {
		c.json(http.statusbadrequest, gin.h{
			"error": err.error(),
		})
		return
	}
	// 根据user_id从数据库查询数据
	u, err := models.getuserbyid(user_id)
	if err != nil {
		c.json(http.statusbadrequest, gin.h{
			"error": err.error(),
		})
		return
	}
	c.json(http.statusok, gin.h{
		"message": "success",
		"data": u,
	})
}

models/user.go文件中实现getuserbyid

// 返回前将用户密码置空
func (u *user) preparegive() {
	u.password = ""
}
func getuserbyid(uid uint) (user, error) {
	var u user
	if err := db.first(&u, uid).error; err != nil {
		return u, errors.new("user not found")
	}
	u.preparegive()
	return u, nil
}

至此,一个简单的gin-jwt应用就完成了。

客户端测试python脚本

服务端的三个接口这里用python脚本来测试

import requests
import json
headers = {
    # "authorization": f"bearer {token}",
    "content-type": "application/json",
}
resp = requests.get("http://127.0.0.1:8000/api/admin/user", headers=headers)
def register(username: str, password: str):
    req_body = {
        "username": username,
        "password": password,
    }
    resp = requests.post("http://127.0.0.1:8000/api/register", data=json.dumps(req_body), headers=headers)
    print(resp.text)
def login(username: str, password: str):
    req_body = {
        "username": username,
        "password": password,
    }
    resp = requests.post("http://127.0.0.1:8000/api/login", data=json.dumps(req_body), headers=headers)
    print(resp.text)
    if resp.status_code == 200:
        return resp.json()["token"]
    else:
        return ""
def test_protect_api(token: str):
    global headers
    headers["authorization"] = f"bearer {token}"
    resp = requests.get("http://127.0.0.1:8000/api/admin/user", headers=headers)
    print(resp.text)
if __name__ == "__main__":
    username = "lisi"
    password = "123456"
    register(username, password)
    token = login(username, password)
    test_protect_api(token)

运行脚本结果

{"message":"register success"}
{"token":"eyjhbgcioijiuzi1niisinr5cci6ikpxvcj9.eyjhdxrob3jpemvkijp0cnvllcjlehaioje3mtk5nda0njasinvzzxjfawqiojz9.qkzn0ot9hab54l3rfbguohhj9oezgia5x_oxppbd2jq"}
{"data":{"id":6,"createdat":"2024-07-03t00:14:20.187725+08:00","updatedat":"2024-07-03t00:14:20.187725+08:00","deletedat":null,"username":"wangwu","password":""},"message":"success"}

完整示例代码

目录结构

├── client.py  # 客户端测试脚本
├── controllers  # 控制器相关包
│   └── auth.go  # 控制器方法实现
├── gin-jwt.bin  # 编译的二进制文件
├── go.mod  # go 项目文件
├── go.sum  # go 项目文件
├── main.go  # 程序入口文件
├── middlewares  # 中间件相关包
│   └── middlewares.go  # 中间件代码文件
├── models  # 存储层相关包
│   ├── setup.go  # 配置数据库连接
│   └── user.go  # user模块相关数据交互的代码文件
├── readme.md  # git repo的描述文件
└── utils  # 工具类包
    └── token  # token相关工具类包
        └── token.go  # token工具的代码文件

main.go

package main
import (
	"log"
	"github.com/gin-gonic/gin"
	"gin-jwt/controllers"
	"gin-jwt/middlewares"
	"gin-jwt/models"
	"github.com/joho/godotenv"
)
func init() {
	err := godotenv.load(".env")
	if err != nil {
		log.fatalf("error loading .env file. %v\n", err)
	}
}
func main() {
	models.connectdatabase()
	r := gin.default()
	public := r.group("/api")
	{
		public.post("/register", controllers.register)
		public.post("/login", controllers.login)
	}
	protected := r.group("/api/admin")
	{
		protected.use(middlewares.jwtauthmiddleware()) // 在路由组中使用中间件
		protected.get("/user", controllers.currentuser)
	}
	r.run("0.0.0.0:8000")
}

controllers

  • auth.go
package controllers
import (
	"net/http"
	"gin-jwt/models"
	"gin-jwt/utils/token"
	"github.com/gin-gonic/gin"
)
// /api/register的请求体
type reqregister struct {
	username string `json:"username" binding:"required"`
	password string `json:"password" binding:"required"`
}
// api/login 的请求体
type reqlogin struct {
	username string `json:"username" binding:"required"`
	password string `json:"password" binding:"required"`
}
func login(c *gin.context) {
	var req reqlogin
	if err := c.shouldbindbodywithjson(&req); err != nil {
		c.json(http.statusbadrequest, gin.h{"error": err.error()})
		return
	}
	u := models.user{
		username: req.username,
		password: req.password,
	}
	// 调用 models.logincheck 对用户名和密码进行验证
	token, err := models.logincheck(u.username, u.password)
	if err != nil {
		c.json(http.statusbadrequest, gin.h{
			"error": "username or password is incorrect.",
		})
		return
	}
	c.json(http.statusok, gin.h{
		"token": token,
	})
}
func register(c *gin.context) {
	var req reqregister
	if err := c.shouldbindbodywithjson(&req); err != nil {
		c.json(http.statusbadrequest, gin.h{
			"data": err.error(),
		})
		return
	}
	u := models.user{
		username: req.username,
		password: req.password,
	}
	_, err := u.saveuser()
	if err != nil {
		c.json(http.statusbadrequest, gin.h{
			"data": err.error(),
		})
		return
	}
	c.json(http.statusok, gin.h{
		"message": "register success",
	})
}
func currentuser(c *gin.context) {
	// 从token中解析出user_id
	user_id, err := token.extracttokenid(c)
	if err != nil {
		c.json(http.statusbadrequest, gin.h{
			"error": err.error(),
		})
		return
	}
	// 根据user_id从数据库查询数据
	u, err := models.getuserbyid(user_id)
	if err != nil {
		c.json(http.statusbadrequest, gin.h{
			"error": err.error(),
		})
		return
	}
	c.json(http.statusok, gin.h{
		"message": "success",
		"data": u,
	})
}

models

  • setup.go
package models
import (
	"fmt"
	"log"
	"os"
	"gorm.io/driver/postgres"
	"gorm.io/gorm"
)
var db *gorm.db
func connectdatabase() {
	var err error
	dbhost := os.getenv("db_host")
	dbport := os.getenv("db_port")
	dbuser := os.getenv("db_user")
	dbpass := os.getenv("db_pass")
	dbname := os.getenv("db_name")
	dsn := fmt.sprintf("host=%s port=%s user=%s dbname=%s sslmode=disable timezone=asia/shanghai password=%s", dbhost, dbport, dbuser, dbname, dbpass)
	db, err = gorm.open(postgres.open(dsn), &gorm.config{})
	if err != nil {
		log.fatalf("connect to database failed, %v\n", err)
	} else {
		log.printf("connect to database success, host: %s, port: %s, user: %s, dbname: %s\n", dbhost, dbport, dbuser, dbname)
	}
	// 迁移数据表
	db.automigrate(&user{})
}
  • user.go
package models
import (
	"errors"
	"gin-jwt/utils/token"
	"html"
	"strings"
	"golang.org/x/crypto/bcrypt"
	"gorm.io/gorm"
)
type user struct {
	gorm.model
	username string `gorm:"size:255;not null;unique" json:"username"`
	password string `gorm:"size:255;not null;" json:"password"`
}
func (u *user) saveuser() (*user, error) {
	err := db.create(&u).error
	if err != nil {
		return &user{}, err
	}
	return u, nil
}
// 使用gorm的hook在保存密码前对密码进行hash
func (u *user) beforesave(tx *gorm.db) error {
	hashedpassword, err := bcrypt.generatefrompassword([]byte(u.password), bcrypt.defaultcost)
	if err != nil {
		return err
	}
	u.password = string(hashedpassword)
	u.username = html.escapestring(strings.trimspace(u.username))
	return nil
}
// 返回前将用户密码置空
func (u *user) preparegive() {
	u.password = ""
}
// 对哈希加密的密码进行比对校验
func verifypassword(password, hashedpassword string) error {
	return bcrypt.comparehashandpassword([]byte(hashedpassword), []byte(password))
}
func logincheck(username, password string) (string, error) {
	var err error
	u := user{}
	err = db.model(user{}).where("username = ?", username).take(&u).error
	if err != nil {
		return "", err
	}
	err = verifypassword(password, u.password)
	if err != nil && err == bcrypt.errmismatchedhashandpassword {
		return "", err
	}
	token, err := token.generatetoken(u.id)
	if err != nil {
		return "", err
	}
	return token, nil
}
func getuserbyid(uid uint) (user, error) {
	var u user
	if err := db.first(&u, uid).error; err != nil {
		return u, errors.new("user not found")
	}
	u.preparegive()
	return u, nil
}

utils

  • token/token.go
package token
import (
	"fmt"
	"os"
	"strconv"
	"strings"
	"time"
	"github.com/gin-gonic/gin"
	"github.com/golang-jwt/jwt/v5"
)
func generatetoken(user_id uint) (string, error) {
	token_lifespan, err := strconv.atoi(os.getenv("token_hour_lifespan"))
	if err != nil {
		return "", err
	}
	claims := jwt.mapclaims{}
	claims["authorized"] = true
	claims["user_id"] = user_id
	claims["exp"] = time.now().add(time.hour * time.duration(token_lifespan)).unix()
	token := jwt.newwithclaims(jwt.signingmethodhs256, claims)
	return token.signedstring([]byte(os.getenv("api_secret")))
}
func tokenvalid(c *gin.context) error {
	tokenstring := extracttoken(c)
	fmt.println(tokenstring)
	_, err := jwt.parse(tokenstring, func(token *jwt.token) (any, error) {
		if _, ok := token.method.(*jwt.signingmethodhmac); !ok {
			return nil, fmt.errorf("unexpected signing method: %v", token.header["alg"])
		}
		return []byte(os.getenv("api_secret")), nil
	})
	if err != nil {
		return err
	}
	return nil
}
// 从请求头中获取token
func extracttoken(c *gin.context) string {
	bearertoken := c.getheader("authorization")
	if len(strings.split(bearertoken, " ")) == 2 {
		return strings.split(bearertoken, " ")[1]
	}
	return ""
}
// 从jwt中解析出user_id
func extracttokenid(c *gin.context) (uint, error) {
	tokenstring := extracttoken(c)
	token, err := jwt.parse(tokenstring, func(token *jwt.token) (any, error) {
		if _, ok := token.method.(*jwt.signingmethodhmac); !ok {
			return nil, fmt.errorf("unexpected signing method: %v", token.header["alg"])
		}
		return []byte(os.getenv("api_secret")), nil
	})
	if err != nil {
		return 0, err
	}
	claims, ok := token.claims.(jwt.mapclaims)
	// 如果jwt有效,将user_id转换为浮点数字符串,然后再转换为 uint32
	if ok && token.valid {
		uid, err := strconv.parseuint(fmt.sprintf("%.0f", claims["user_id"]), 10, 32)
		if err != nil {
			return 0, err
		}
		return uint(uid), nil
	}
	return 0, nil
}

middlewares

  • middlewares.go
package middlewares
import (
	"gin-jwt/utils/token"
	"net/http"
	"github.com/gin-gonic/gin"
)
func jwtauthmiddleware() gin.handlerfunc {
	return func(c *gin.context) {
		err := token.tokenvalid(c)
		if err != nil {
			c.string(http.statusunauthorized, err.error())
			c.abort()
			return
		}
		c.next()
	}
}

参考

到此这篇关于golang 在gin框架中使用jwt鉴权的文章就介绍到这了,更多相关golang gin框架使用jwt鉴权内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2025  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com