博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Gin实践 连载五 使用JWT进行身份校验
阅读量:7166 次
发布时间:2019-06-29

本文共 7262 字,大约阅读时间需要 24 分钟。

使用JWT进行身份校验

原文地址:

在前面几节中,我们已经基本的完成了API's的编写

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

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

项目地址:


下载依赖包

首先,我们下载jwt-go的依赖包

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

编写jwt工具包

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

package utilimport (    "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}

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

  • NewWithClaims(method SigningMethod, claims Claims)method对应着SigningMethodHMAC struct{},其包含SigningMethodHS256SigningMethodHS384SigningMethodHS512三种crypto.Hash方案
  • func (t *Token) SignedString(key interface{}) 该方法内部生成签名字符串,再用于获取完整、已签名的token
  • func (p *Parser) ParseWithClaims 用于解析鉴权的声明,主要是具体的解码和校验的过程,最终返回*Token
  • func (m MapClaims) Valid() 验证基于时间的声明exp, iat, nbf,注意如果没有任何声明在令牌中,仍然会被认为是有效的。并且对于时区偏差没有计算方法

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

package jwtimport (    "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 modelstype 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 apiimport (    "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 routersimport (    "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

获取token的API方法就到这里啦,让我们来测试下是否可以正常使用吧!

重启服务后,用GET方式访问http://127.0.0.1:8000/auth?username=test&password=test123456,查看返回值是否正确

{  "code": 200,  "data": {    "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6InRlc3QiLCJwYXNzd29yZCI6InRlc3QxMjM0NTYiLCJleHAiOjE1MTg3MjAwMzcsImlzcyI6Imdpbi1ibG9nIn0.-kK0V9E06qTHOzupQM_gHXAGDB3EJtJS4H5TTCyWwW8"  },  "msg": "ok"}

我们有了token的API,也调用成功了

将中间件接入Gin

2、 接下来我们将中间件接入到Gin的访问流程中

我们打开routers目录下的router.go文件,修改文件内容(新增引用包和中间件引用)

package routersimport (    "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

到这里,我们的JWT编写就完成啦!

验证功能

我们来测试一下,再次访问

  • :8000/api/v1/articles
  • :8000/api/v1/articles?token=23131

正确的反馈应该是

{  "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.eyJ1c2VybmFtZSI6InRlc3QiLCJwYXNzd29yZCI6InRlc3QxMjM0NTYiLCJleHAiOjE1MTg3MjQ2OTMsImlzcyI6Imdpbi1ibG9nIn0.KSBY6TeavV_30kfmP7HWLRYKP5TPEDgHtABe9HCsic4"  },  "msg": "ok"}

再用包含token的URL参数去访问我们的应用API,

访问http://127.0.0.1:8000/api/v1/articles?token=eyJhbGci...,检查接口返回值

{  "code": 200,  "data": {    "lists": [      {        "id": 2,        "created_on": 1518700920,        "modified_on": 0,        "tag_id": 1,        "tag": {          "id": 1,          "created_on": 1518684200,          "modified_on": 0,          "name": "tag1",          "created_by": "",          "modified_by": "",          "state": 0        },        "content": "test-content",        "created_by": "test-created",        "modified_by": "",        "state": 0      }    ],    "total": 1  },  "msg": "ok"}

返回正确,至此我们的jwt-goGin中的验证就完成了!

本系列目录

转载地址:http://gxtwm.baihongyu.com/

你可能感兴趣的文章