Gin框架实现JWT认证(加解密)的两种写法

最近在搭公司的项目(之前的旧项目),用Gin框架实现JWT的认证,加密解密,第一种写法是上一个go项目的写法拿来分享,第二种写法是在网上找的写法已调试通过;两种写法其实是一样的,都需要引入JWT包 “github.com/dgrijalva/jwt-go”
第一种写法:
路由文件 router.go

package router
import (
	"errors"
	"project/api/controller"
	"fmt"
	"github.com/dgrijalva/jwt-go"
	"github.com/gin-gonic/gin"
	"github.com/spf13/cast"
)
func InitRouter() *gin.Engine  {
	router := gin.Default()
	router.POST("/login", controller.Login) //第一种写法的login
	router.POST("/login_two", controller.LoginTwo) //第二种写法的login
	//路由分组
	apiRoutes := router.Group("/api", setUserStatus())
	{  //第一种写法  解密JWT数据json输出
		apiRoutes.GET("/v1/me", controller.GetUserInfo)
	}
	//解析token 后的API
	apiRoutesTwo := router.Group("/api_new", setUserStatusTwo())
	{
		// 第二种写法  解密JWT数据json输出
		apiRoutesTwo.GET("/v1/me_two", controller.GetUserInfoTwo)
	}
	return router
}


LoginController.go  登录控制器文件
package controller

import (
	"encoding/json"  //json格式数据
	"fmt"
	"github.com/go-playground/validator/v10" //验证输入参数
	"github.com/dgrijalva/jwt-go" //JWT 包
	"project/utils/response"  //返回数据 封装
	"time"
	"project/requests"
	"github.com/gin-gonic/gin"
	"net/http"
	"github.com/patrickmn/go-cache"  //本地缓存
)
func Login(c *gin.Context){
	utilGin := response.Gin{Ctx: c}
	var loginRequest requests.LoginRequest //这里省去 Json格式POST提交
	if err := c.ShouldBindJSON(&loginRequest); err != nil {
		switch err.(type) {
		case *json.UnmarshalTypeError:
			fmt.Println(err.(*json.UnmarshalTypeError))
			utilGin.Response(http.StatusBadRequest, "参数格式异常", nil)
			return
		case validator.ValidationErrors:
			errs := err.(validator.ValidationErrors)
			utilGin.Response(http.StatusBadRequest, "参数异常", loginRequest.GetError(errs))
			return
		}
		utilGin.Response(http.StatusBadRequest, "参数错误", nil)
		return
	}
	//这里省去 获取用户(依据username) 信息从表里查询
	res := make(map[string]string) //定义一个map返回结果
	//引入jwt 生成token
	token := jwt.New(jwt.SigningMethodHS256) //输出:&{ 0xc00045e0a0 map[alg:HS256 typ:JWT] map[exp:1597576334 sub:1]  false}
	var claims map[string]interface{}
	claims = token.Claims.(jwt.MapClaims)
	claims["user_id"] = fmt.Sprintf("%d", 119)   //从数据库中取出
	claims["user_name"] = fmt.Sprintf("%s", "chenhaibo")  //从数据库中取出
	claims["user_mobile"] = fmt.Sprintf("%s", "18217225076")  //从数据库中取出
	claims["exp"] = time.Now().Add(time.Hour * 168).Unix()   //exp:1597576334  暂定有效168小时
	fmt.Print(claims)  // map[exp:1597756825 user_id:1 user_mobile:18217225076 user_name:陈海波]
	var mySigningKey = []byte(config.Get("JwtSecret")) //自己定义一个秘钥  解密那边也用到
	tokenStr, err := token.SignedString(mySigningKey)
	if err != nil{
		utilGin.Response(http.StatusBadRequest, "登录异常,请稍后再试", nil)
		return
	}
	res["username"] = loginRequest.Username
	res["password"] = loginRequest.Password
	res["token"] = tokenStr
	utilGin.Response(0, "success", res)
} //运行  go run .    后访问localhost:5050/login
{ //postaman里 POST请求:
    "Username": "18217225076",
    "Password":"18217225076"
}
{
    "code": 0,
    "msg": "success",
    "data": {
        "password": "18217225076",
        "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1OTk3NDQ0NzUsInVzZXJfaWQiOiIxIiwidXNlcl9tb2JpbGUiOiIxODIxNzIyNTA3NiIsInVzZXJfbmFtZSI6IumZiOa1t-azoiJ9.ovHJhSIsa3WxrJpPdBmoG6_QbDRmhw6V0_phyZ4xkDY",
        "username": "18217225076"
    }
}
//第一种写法解密:我就临时写在路由router.go文件里(因为接口验证是否登录,类似中间件里做验证)
// 该中间件设置用户是否登录 上面 router.Group("/api", setUserStatus())的setUserStatus()
func setUserStatus() gin.HandlerFunc {
	return func(c *gin.Context) {
		var authorization = c.Query("token") //url方式过来的
		var tokenStr string
		if authorization != "" {
			tokenStr = authorization
		} else { //请求header头过来,大部分是这种方式带过来的 postman里设置一下
			authorization = c.Request.Header.Get("Authorization")
			if len(authorization) > 7 {
				tokenStr = authorization[7:]
			}
		}
		if tokenStr != "" {
			var mySigningKey = []byte(config.Get("JwtSecret"))  //登录方法里的那个秘钥
			token, err := jwt.Parse(tokenStr, func(token *jwt.Token) (interface{}, error) {
				if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
					return nil, fmt.Errorf("There was an error")
				}
				return mySigningKey, nil
			})
			if err == nil {
				if token.Valid {
					claimsToken := token.Claims
					var claims map[string]interface{}
					claims = claimsToken.(jwt.MapClaims)

					userId := cast.ToInt(claims["user_id"])
					userName := cast.ToString(claims["user_name"])
					userMobile := cast.ToString(claims["user_mobile"])
					fmt.Print(userId)
					c.Set("logged_user_id", userId)  //存储在本地缓存里
					c.Set("logged_user_name", userName)
					c.Set("logged_user_mobile", userMobile)
					c.Set("is_logged_in", true)
					return

				}
			}
		}
		c.Set("is_logged_in", false)
	}
}
//User控制器 获取 通过缓存
func GetUserInfo(c *gin.Context)  {
	utilGin := response.Gin{Ctx: c}
	res := make(map[string]interface{})
	user_id,_ := c.Get("logged_user_id")  // c.GetInt
	user_name,_ := c.Get("logged_user_name")  // c.GetString
	user_mobile,_ := c.Get("logged_user_mobile")
	res["user_id"] = user_id
	res["user_name"] = user_name
	res["user_mobile"] = user_mobile
	utilGin.Response(0, "success", res)
}  //返回结果
{
    "code": 0,
    "msg": "success",
    "data": {
        "user_id": 119,
        "user_mobile": "18217225076",
        "user_name": "chenhaibo"
    }
}

第二种写法:

func LoginTwo(c *gin.Context){
	utilGin := response.Gin{Ctx: c}
	var loginRequest requests.LoginRequest
	//只有绑定c.ShouldBindJSON(&loginRequest) 才能获取到body格式的POST过来的参数
	if err := c.ShouldBindJSON(&loginRequest); err != nil {

		switch err.(type) {
		case *json.UnmarshalTypeError:
			fmt.Println(err.(*json.UnmarshalTypeError))
			utilGin.Response(http.StatusBadRequest, "参数格式异常,请检查参数列表中的参数数据类型是否正确", nil)
			return
		case validator.ValidationErrors:
			errs := err.(validator.ValidationErrors)
			utilGin.Response(http.StatusBadRequest, "参数异常", loginRequest.GetError(errs))
			return
		}
		utilGin.Response(http.StatusBadRequest, "参数错误", nil)
		return
	} //获取参数: var username = loginRequest.Username
	res := make(map[string]string)
	//生成token
	claim := jwt.MapClaims{
		"user_id":  	119,
		"user_name": 	"chenhaibo119",
		"login_time":   time.Now().Unix(),
		"expire_time":  time.Now().Add(time.Hour * 2).Unix(),  //2小时候过期
	}
	token_tmp := jwt.NewWithClaims(jwt.SigningMethodHS256,claim)
	token,err  := token_tmp.SignedString(secret()) //config.Get("JwtSecret")  秘钥
	if err != nil{
		utilGin.Response(http.StatusUnauthorized, "生成JWT错误", err)
		return
	}
	res["username"] = loginRequest.Username
	res["password"] = loginRequest.Password
	res["token"] 	= token
	utilGin.Response(0, "success", res)
}  //postman请求省去   返回结果:
{
    "code": 0,
    "msg": "success",
    "data": {
        "password": "18217225076",
        "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHBpcmVfdGltZSI6MTU5OTE0Nzc5NCwibG9naW5fdGltZSI6MTU5OTE0MDU5NCwidXNlcl9pZCI6MTE5LCJ1c2VyX25hbWUiOiJjaGVuaGFpYm8xMTkifQ.StefJNCT4FpxbkLYwOzx5hnhjJntTt-LpUWAvlNTdls",
        "username": "18217225076"
    }
}
//解密   临时放到路由文件里(应该放在middle中间件目录下)
func setUserStatusTwo() gin.HandlerFunc {
	return func(c *gin.Context) {
		var authorization = c.Query("token")
		var tokenStr string
		if authorization != "" {
			tokenStr = authorization
		} else {
			authorization = c.Request.Header.Get("Authorization")
			if len(authorization) > 7 {
				tokenStr = authorization[7:]
			}
		}
		if tokenStr != "" {
			token,err := jwt.Parse(tokenStr,secret())
			if err != nil{
				return
			}
			claim,ok := token.Claims.(jwt.MapClaims)
			if !ok{
				err = errors.New("cannot convert claim to mapclaim")
				return
			}
			//验证token,如果token被修改过则为false
			if  !token.Valid{
				err = errors.New("token is invalid")
				return
			}
			if err == nil {

				userId := cast.ToInt(claim["user_id"])
				userName := cast.ToString(claim["user_name"])
				login_time  := cast.ToString(claim["login_time"])
				expire_time := cast.ToString(claim["expire_time"])
				c.Set("logged_user_id", userId)  //存储在本地缓存
				c.Set("logged_user_name", userName)
				c.Set("logged_user_time", login_time)
				c.Set("logged_user_expire_time", expire_time)
				c.Set("is_logged_in", true)
				return
			}
		}
		c.Set("is_logged_in", false)
	}
}
func secret()jwt.Keyfunc{
	return func(token *jwt.Token) (interface{}, error) {
		return []byte("这里是秘钥"),nil
	}
}
//控制器里验证一下:
func GetUserInfoTwo(c *gin.Context)  {
	utilGin := response.Gin{Ctx: c}

	res := make(map[string]interface{})
	user_id,_ 				:= c.Get("logged_user_id")  // c.GetInt
	user_name,_ 			:= c.Get("logged_user_name")  // c.GetString
	login_time,_ 			:= c.Get("logged_user_time")
	logged_expire_time,_ 	:= c.Get("logged_user_expire_time")
	user_logined,_ 			:= c.Get("is_logged_in")
	res["user_id"] 		= user_id
	res["user_name"] 	= user_name
	res["login_time"] 	= login_time
	res["expire_time"] 	= logged_expire_time
	res["user_logined"] = user_logined
	utilGin.Response(0, "success", res)
}
{
    "code": 0,
    "msg": "success",
    "data": {
        "expire_time": "1599147794",
        "login_time": "1599140594",
        "user_id": 119,
        "user_logined": true,
        "user_name": "chenhaibo119"
    }
}

其实两种写法一样,返回的结果一样,同样的token两种方式解密出的结果一样;当然还有很多需要优化封装的地方;


版权声明:本文为u014459543原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。