go

gin+gorm HelloWorld(使用JWT进行身份校验)

2019-02-26  本文已影响0人  我就是小政政

使用JWT进行身份校验

但是,还存在一些非常严重的问题,例如,我们现在的API是可以随意调用的,这显然还不够完美,是有问题的

那么我们采用 jwt-goGoDoc)的方式来简单解决这个问题

下载依赖包

go get -u github.com/dgrijalva/jwt-go

编写jwt工具包

我们需要编写一个jwt的工具包,我们在pkg下的util目录新建jwt.go,写入文件内容:

package util

import (
    "time"

    jwt "github.com/dgrijalva/jwt-go"

    "gin-blog/pkg/setting"
)

var jwtSecret = []byte(setting.JwtSecret)

type Claims struct {
    Username string `json:"username"`
    Password string `json:"password"`
    jwt.StandardClaims
}

func GenerateToken(username, password string) (string, error) {
    nowTime := time.Now()
    expireTime := nowTime.Add(3 * time.Hour)

    claims := Claims{
        username,
        password,
        jwt.StandardClaims {
            ExpiresAt : expireTime.Unix(),
            Issuer : "gin-blog",
        },
    }

    tokenClaims := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
    token, err := tokenClaims.SignedString(jwtSecret)

    return token, err
}

func ParseToken(token string) (*Claims, error) {
    tokenClaims, err := jwt.ParseWithClaims(token, &Claims{}, func(token *jwt.Token) (interface{}, error) {
        return jwtSecret, nil
    })

    if tokenClaims != nil {
        if claims, ok := tokenClaims.Claims.(*Claims); ok && tokenClaims.Valid {
            return claims, nil
        }
    }

    return nil, err
}

在这个工具包,我们涉及到

有了jwt工具包,接下来我们要编写要用于Gin的中间件,我们在middleware下新建jwt目录,新建jwt.go文件,写入内容:

package jwt

import (
    "time"
    "net/http"

    "github.com/gin-gonic/gin"

    "gin-blog/pkg/util"
    "gin-blog/pkg/e"
)

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 {
            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()
    }
}

这个中间件涉及到:

如何获取Token

那么我们如何调用它呢,我们还要获取Token呢?
1、 我们要新增一个获取Token的API
在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)
    if auth.ID > 0 {
        return true
    }

    return false
}

在routers下的api目录新建auth.go文件,写入内容:

package api

import (
    "log"
    "net/http"

    "github.com/gin-gonic/gin"
    "github.com/astaxie/beego/validation"

    "gin-blog/pkg/e"
    "gin-blog/pkg/util"
    "gin-blog/models"
)

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, 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文件,修改文件内容(新增获取token的方法):

package routers

import (
    "github.com/gin-gonic/gin"
    
    "gin-blog/routers/api"
    "gin-blog/routers/api/v1"
    "gin-blog/pkg/setting"
)

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
}
验证Token

访问 GET http://localhost:8000/auth?username=test&password=test123456

{
    "code": 200,
    "data": {
        "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6InRlc3QiLCJwYXNzd29yZCI6InRlc3QxMjM0NTYiLCJleHAiOjE1NTExNzk4MzEsImlzcyI6Imdpbi1ibG9nIn0.bE0agND6A_fyyZz_UeR_YtdBvOrC2pjTPtL47SuAYWI"
    },
    "msg": "ok"
}
将中间件接入Gin

2、 接下来我们将中间件接入到Gin的访问流程中
我们打开routers目录下的router.go文件,修改文件内容(新增引用包和中间件引用)

package routers

import (
    "github.com/gin-gonic/gin"
    
    "gin-blog/routers/api"
    "gin-blog/routers/api/v1"
    "gin-blog/pkg/setting"
    "gin-blog/middleware/jwt"
)

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
}

当前结构目录
gin-blog/
├── conf
│   └── app.ini
├── main.go
├── middleware
│   └── jwt
│       └── jwt.go
├── models
│   ├── article.go
│   ├── auth.go
│   ├── models.go
│   └── tag.go
├── pkg
│   ├── e
│   │   ├── code.go
│   │   └── msg.go
│   ├── setting
│   │   └── setting.go
│   └── util
│       ├── jwt.go
│       └── pagination.go
├── routers
│   ├── api
│   │   ├── auth.go
│   │   └── v1
│   │       ├── article.go
│   │       └── tag.go
│   └── router.go
├── runtime
验证功能
{
    "code": 400,
    "data": null,
    "msg": "请求参数错误"
}
{
    "code": 20001,
    "data": null,
    "msg": "Token鉴权失败"
}

我们需要访问http://127.0.0.1:8000/auth?username=test&password=test123456,得到token

{
    "code": 200,
    "data": {
        "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6InRlc3QiLCJwYXNzd29yZCI6InRlc3QxMjM0NTYiLCJleHAiOjE1NTExODA1MjgsImlzcyI6Imdpbi1ibG9nIn0.dcbLkez5QDrgierHmWWYMRBoyLPawSsLew32C_IARz0"
    },
    "msg": "ok"
}

再用包含token的URL参数去访问我们的应用API,
访问http://127.0.0.1:8000/api/v1/articles?token=eyJhbGci...,检查接口返回值

{
    "code": 200,
    "data": {
        "lists": [
            {
                "id": 1,
                "created_on": 0,
                "modified_on": 0,
                "title": "夸父逐日",
                "desc": "\b夸父锲而不舍追求太阳最终失败的\b\b故事",
                "content": "\baaabbbcccdddeee夸父",
                "created_by": "test",
                "modified_by": "",
                "state": 1,
                "tag_id gorm:": 4,
                "tag": {
                    "id": 4,
                    "created_on": 1551077861,
                    "modified_on": 0,
                    "name": "百科全书",
                    "created_by": "test",
                    "modified_by": "",
                    "state": 1
                }
            }
        ],
        "total": 1
    },
    "msg": "ok"
}

jwt与session方案对比等参考

九幅图理解使用JSON Web Token(JWT)设计单点登录系统
https://blog.csdn.net/uniquewonderq/article/details/79720897
使用JWT做微服务的登录方案 https://blog.csdn.net/w57685321/article/details/79463837

上一篇下一篇

猜你喜欢

热点阅读