• Gin框架使用jwtgo配合中间件认证


    参考文档

    // 文档
    https://github.com/golang-jwt/jwt
    https://pkg.go.dev/github.com/golang-jwt/jwt@v3.2.2+incompatible#example-NewWithClaims-CustomClaimsType
    https://gin-gonic.com/zh-cn/docs/examples/using-middleware/
    https://gin-gonic.com/zh-cn/docs/examples/custom-middleware/
    

    下载

    // 下载
    go get -u github.com/golang-jwt/jwt
    

    实战

    // util/jwt.go
    package util
    
    import (
    	"gindemo/pkg/setting"
    	"time"
    
    	"github.com/golang-jwt/jwt"
    )
    
    var jwtSecret = []byte(setting.JwtSecret) //配置文件中自己配置的
    
    // Claims是一些用户信息状态和额外的jwt参数
    type Claims struct {
    	Username string `json:"username"`
    	Password string `json:"password"`
    	jwt.StandardClaims
    }
    
    // 根据用户的用户名和密码参数token
    func GenerateToken(username, password string) (string, error) {
    	nowTime := time.Now()
    	expireTime := nowTime.Add(time.Minute * 15).Unix()
    
    	claims := Claims{
    		Username: username,
    		Password: password,
    		StandardClaims: jwt.StandardClaims{
    			ExpiresAt: expireTime, // 过期时间
    			Issuer:    "gindemo",  //指定发行人
    		},
    	}
    	// 该方法内部生成签名字符串,再用于获取完整、已签名的token
    	tokenClaims := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
    	token, err := tokenClaims.SignedString(jwtSecret)
    	return token, err
    }
    
    // 根据传入的token值获取到Claims对象信息(进而获取其中的用户名和密码)
    func ParseToken(token string) (*Claims, error) {
    	// 用于解析鉴权的声明,方法内部主要是具体的解码和校验的过程,最终返回*Token
    	tokenClaims, err := jwt.ParseWithClaims(token, &Claims{}, func(token *jwt.Token) (interface{}, error) {
    		return jwtSecret, nil
    	})
    	if tokenClaims != nil {
    		// 从tokenClaims中获取到Claims对象,并使用断言,将该对象转换为我们自己定义的Claims
    		// 要传入指针,项目结构体都是用指针传递,节省空间
    		if claims, ok := tokenClaims.Claims.(*Claims); ok && tokenClaims.Valid { // Valid()验证基于时间的声明
    			return claims, nil
    		}
    	}
    	return nil, err
    }
    
    
    
    // 中间件
    // middleware/jwt/jwt.go
    package jwt
    
    import (
    	"gindemo/pkg/e"
    	"gindemo/pkg/util"
    	"net/http"
    	"time"
    
    	"github.com/gin-gonic/gin"
    )
    
    // 自定义中间件
    func JWT() gin.HandlerFunc {
    	return func(c *gin.Context) {
    		var code int
    		var data interface{}
    
    		code = e.SUCCESS
    		token := c.Query("token")
    		if token == "" {
    			code = e.INVALID_PARAMS
    		} else {
                            // 解析token
    			claims, err := util.ParseToken(token)
    			if err != nil {
    				code = e.ERROR_AUTH_CHECK_TOKEN_FAIL
    			} else if time.Now().Unix() > claims.ExpiresAt {
    				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
    		}
    		c.Next()
    	}
    }
    
    
    // models/auth.go
    // 数据库查用户
    package models
    
    type Auth struct {
    	ID       int    `gorm:"primary_key" json:"id"`
    	Username string `json:"username"`
    	Password string `json:"password"`
    }
    
    func CheckAuth(username, password string) bool {
    	var auth Auth
    	db.Select("id").Where(Auth{Username: username, Password: password}).First(&auth)
    	return auth.ID > 0
    }
    
    
    // routers/api/auth.go
    // 用户认证逻辑视图
    package api
    
    import (
    	"gindemo/models"
    	"gindemo/pkg/e"
    	"gindemo/pkg/util"
    	"log"
    	"net/http"
    
    	"github.com/astaxie/beego/validation"
    	"github.com/gin-gonic/gin"
    )
    
    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 := validation.Validation{}
    
    	a := auth{Username: username, Password: password}
    	ok, _ := valid.Valid(&a)
    
    	data := make(map[string]interface{})
    	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 {
    				data["token"] = token
    				code = e.SUCCESS
    			}
    		} else {
    			code = e.ERROR_AUTH
    		}
    	} else {
    		for _, err := range valid.Errors {
    			log.Println(err.Key, err.Message)
    		}
    	}
    
    	c.JSON(http.StatusOK, gin.H{
    		"code": code,
    		"msg":  e.GetMsg(code),
    		"data": data,
    	})
    }
    
    
    // routers/router.go
    // 添加auth路由
    package routers
    
    import (
    	"gindemo/middleware/jwt"
    	"gindemo/pkg/setting"
    	"gindemo/routers/api"
    	v1 "gindemo/routers/api/v1"
    
    	"github.com/gin-gonic/gin"
    )
    
    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
    }
    
    
    // 测试
    http://127.0.0.1:8000/auth?username=test&password=test123456
    // 返回数据
    {
        "code": 200,
        "data": {
            "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6InRlc3QiLCJwYXNzd29yZCI6InRlc3QxMjM0NTYiLCJleHAiOjE2NDYzMzc0NzMsImlzcyI6ImdpbmRlbW8ifQ.mFGXb6dyYFGIni3joNinfpsNmeDAvvDOFKSfvJ4ss1w"
        },
        "msg": "ok"
    }
    
    
    
    // 将中间件接入Gin
    // routers/router.go
    package routers
    
    import (
    	"gindemo/middleware/jwt"
    	"gindemo/pkg/setting"
    	"gindemo/routers/api"
    	v1 "gindemo/routers/api/v1"
    
    	"github.com/gin-gonic/gin"
    )
    
    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")
    	// 使用中间件认证
    	apiv1.Use(jwt.JWT())
        
    	{
    		...
    	}
    
    	return r
    }
    
    
    // 以后每次请求的时候先获取Token,然后请求其他url的时候带上token就可以了
    
  • 相关阅读:
    discuz方法赏析
    rpm安装软件(需管理员权限)
    php.ini修改php上传文件大小限制
    grep 基于关键字搜索
    网络排查的原则
    Sublime Text快捷键
    表格无边框,有内框,在table嵌套时,防止出现重复边线
    文件(图片)强制下载
    网络相关配置文件的位置
    ip,子网掩码,网关,DNS
  • 原文地址:https://www.cnblogs.com/weiweivip666/p/15962739.html
Copyright © 2020-2023  润新知