最近在搭公司的项目(之前的旧项目),用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两种方式解密出的结果一样;当然还有很多需要优化封装的地方;