最近在搭公司的项目(之前的旧项目),用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版权协议,转载请附上原文出处链接和本声明。