This is a creation in Article, where the information may have evolved or changed.
API ' S-json Web Tokens (JWT)
In the previous sections, we have basically completed the writing of the API ' s
However, there are some very serious problems, for example, our current API can be arbitrarily called, which is obviously not perfect enough, there is a problem
Then we use Jwt-go (Godoc) To solve this problem in a simple way.
Download Dependent packages
First, we download the Jwt-go dependency package
go get -u github.com/dgrijalva/jwt-go
Authoring jwt
Toolkit
We need to write a jwt
toolkit that we created in the pkg
next util
directory jwt.go
to write the contents of the file:
Package Utilimport ("Time" JWT "Github.com/dgrijalva/jwt-go" "gin-blog/pkg/setting") var jwtsecret = []byte (Setti Ng. 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 (toke n String (*claims, error) {tokenclaims, err: = JWT. Parsewithclaims (token, &claims{}, func (token *JWT). Token) (interface{}, error) {return jwtsecret, nil}) if tokenclaims! = nil {if claims, OK: = Tokenc Laims. Claims. (*claims); OK && tokenclaims. Valid {return claims, nil}} return nil, err}
In this toolkit, we relate
NewWithClaims(method SigningMethod, claims Claims)
, which method
corresponds SigningMethodHMAC struct{}
to, including SigningMethodHS256
, SigningMethodHS384
SigningMethodHS512
Three kinds of crypto.Hash
solutions
func (t *Token) SignedString(key interface{})
The method internally generates the signature string, which is then used to obtain the full, signedtoken
func (p *Parser) ParseWithClaims
A declaration used for parsing authentication, which is essentially a process of decoding and verifying, ultimately returning*Token
func (m MapClaims) Valid()
Verify time-based claims exp, iat, nbf
, and note that if there are no claims in the token, it will still be considered valid. And there is no calculation method for time zone deviations
With the jwt
toolkit, we're going to write the middleware we're going to use Gin
, we'll middleware
create a new jwt
directory, create a new jwt.go
file, write the content:
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() }}
How to getToken
So how do we call it, and what else do we get Token
?
1, we want to add a Get Token
API
models
under Create a new auth.go
file, write the content:
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}
In the routers
next api
directory, create a new auth.go
file, write the content:
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 () "' Password string ' valid: Required; MaxSize () "'}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,})}
We open the routers
file in the directory router.go
, modify the contents of the file (new method to obtain 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}
VerifyToken
Get token
The API method is here, let us test whether it can be used properly!
After restarting the service, use GET
the method http://127.0.0.1:8000/auth?username=test&password=test123456
to access and see if the return value is correct
{ "code": 200, "data": { "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6InRlc3QiLCJwYXNzd29yZCI6InRlc3QxMjM0NTYiLCJleHAiOjE1MTg3MjAwMzcsImlzcyI6Imdpbi1ibG9nIn0.-kK0V9E06qTHOzupQM_gHXAGDB3EJtJS4H5TTCyWwW8" }, "msg": "ok"}
We have token
the API, and also the call succeeded
Access to middlewareGin
2. Next, we will access the middleware into Gin
the access process
We open the routers
file in the directory router.go
, modify the contents of the file (new reference package and middleware reference)
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}
Current directory structure:
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
Here, our JWT
writing is done!
Validation features
Let's test it and visit it again.
- Http://127.0.0.1:8000/api/v1/articles
- http://127.0.0.1:8000/api/v1/articles?token=23131
The correct feedback should be
{ "code": 400, "data": null, "msg": "请求参数错误"}{ "code": 20001, "data": null, "msg": "Token鉴权失败"}
We need access http://127.0.0.1:8000/auth?username=test&password=test123456
to gettoken
{ "code": 200, "data": { "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6InRlc3QiLCJwYXNzd29yZCI6InRlc3QxMjM0NTYiLCJleHAiOjE1MTg3MjQ2OTMsImlzcyI6Imdpbi1ibG9nIn0.KSBY6TeavV_30kfmP7HWLRYKP5TPEDgHtABe9HCsic4" }, "msg": "ok"}
Then use token
the included URL parameter to access our app API,
Access http://127.0.0.1:8000/api/v1/articles?token=eyJhbGci...
, check the interface return value
{ "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"}
Back to the right, so our jwt-go
verification in in is Gin
done!
Reference
Sample code for this series