Gin-Gonic 使用Goroutine 当在中间件或 handler 中启动新的 Goroutine 时,不能使用原始的上下文,必须使用只读副本。
1 2 3 4 5 6 7 8 9 10 11 12 13 r := gin.Default() r.GET("/long_async" , func (c *gin.Context) { cCp := c.Copy() go func () { time.Sleep(5 * time.Second) log.Println("Done! in path " + cCp.Request.URL.Path) }() })
绑定Uri 1 2 3 4 5 6 7 8 9 10 11 12 func main () { route := gin.Default() route.GET("/:name/:id" , func (c *gin.Context) { var person Person if err := c.ShouldBindUri(&person); err != nil { c.JSON(400 , gin.H{"msg" : err.Error()}) return } c.JSON(200 , gin.H{"name" : person.Name, "uuid" : person.ID}) }) route.Run(":8088" ) }
绑定数据到结构体 非嵌套 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 type Person struct { Name string `form:"name"` Address string `form:"address"` Birthday time.Time `form:"birthday" time_format:"2006-01-02" time_utc:"1"` } func startPage (c *gin.Context) { var person Person if c.ShouldBind(&person) == nil { log.Println(person.Name) log.Println(person.Address) log.Println(person.Birthday) } c.String(200 , "Success" ) }
嵌套 仅支持没有 form 的嵌套结构体。
支持
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 type StructA struct { FieldA string `form:"field_a"` } type StructB struct { NestedStruct StructA FieldB string `form:"field_b"` } type StructC struct { NestedStructPointer *StructA FieldC string `form:"field_c"` } type StructD struct { NestedAnonyStruct struct { FieldX string `form:"field_x"` } FieldD string `form:"field_d"` }
不支持
1 2 3 4 5 6 7 8 9 10 11 type StructX struct { X struct {} `form:"name_x"` // 有 form } type StructY struct { Y StructX `form:"name_y"` // 有 form } type StructZ struct { Z *StructZ `form:"name_z"` // 有 form }
自定义中间件 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 func Logger() gin.HandlerFunc { return func(c *gin.Context) { t := time.Now() // 设置 example 变量 c.Set("example", "12345") // 请求前 c.Next() // 请求后 latency := time.Since(t) log.Print(latency) // 获取发送的 status status := c.Writer.Status() log.Println(status) } } ------------- r.Use(Logger())
常见中间件 jwt https://www.ruanyifeng.com/blog/2018/07/json_web_token-tutorial.html
Base64URL算法将jwt的header、payload加密header里面的加密算法(默认HS256):
1 2 3 4 HMACSHA256( base64UrlEncode(header) + "." + base64UrlEncode(payload), secret)
得到签名,防止篡改数据
客户端收到服务器返回的 JWT,可以储存在 Cookie 里面,也可以储存在 localStorage。 此后,客户端每次与服务器通信,都要带上这个 JWT。你可以把它放在 Cookie 里面自动发送,但是这样不能跨域,所以更好的做法是放在 HTTP 请求的头信息Authorization字段里面。另一种做法是,跨域的时候,JWT 就放在 POST 请求的数据体里面。
比较大的缺点: JWT 本身包含了认证信息,一旦泄露,任何人都可以获得该令牌的所有权限。为了减少盗用,JWT 的有效期应该设置得比较短。对于一些比较重要的权限,使用时应该再次对用户进行认证。为了减少盗用,JWT 不应该使用 HTTP 协议明码传输,要使用 HTTPS 协议传输。
Gin当中: 当 用户登录 时,根据 用户信息 生成 token码,并将 token码 传递给 前端。 当用户再次发送请求时,请求连接中会包含用户对应的 token码,JWT中间件 会在接收到请求之后自动从 token码 中解析出用户信息,并放入请求的上下文 c 中。
Example:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 var secretKey = []byte("stumap") type AuthClaims struct { UserId uint64 `json:"userId"` jwt.StandardClaims } // ParseToken 解析请求头中的 token string,转换成被解析后的 jwt.Token func ParseToken(tokenStr string) (*jwt.Token, error) { // 解析 token string 拿到 token jwt.Token return jwt.ParseWithClaims(tokenStr, &AuthClaims{}, func(tk *jwt.Token) (interface{}, error) { return secretKey, nil }) } func JWTAuth() gin.HandlerFunc { return func(ctx *gin.Context) { tokenStr := ctx.GetHeader("Authorization") if tokenStr == "" { res := response.BaseResponse{Result: "Permission denied", ResultCode: 0} ctx.JSON(http.StatusOK, res) ctx.Abort() return } log.Println("token: ", tokenStr) token, err := ParseToken(tokenStr) if err != nil { res := response.BaseResponse{Result: "Invalid Token.", ResultCode: 0} ctx.JSON(http.StatusForbidden, res) ctx.Abort() return } claims, ok := token.Claims.(*AuthClaims) if !ok { res := response.BaseResponse{Result: "Invalid Token.", ResultCode: 0} ctx.JSON(http.StatusForbidden, res) ctx.Abort() return } ctx.Set("userId", claims.UserId) ctx.Next() } }
Get the Token(When Log in successfully):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 /////////TEST GET TOKEN var expireTime = time.Now().Add(60 * 24 * time.Hour) claim := AuthClaims{ UserId: 1, StandardClaims: jwt.StandardClaims{ ExpiresAt: expireTime.Unix(), IssuedAt: time.Now().Unix(), Issuer: "stumap", Subject: "gindemo", }, } testNosignedToken := jwt.NewWithClaims(jwt.SigningMethodES256, claim) testOKToken, err := testNosignedToken.SignedString(secretKey) if err != nil { log.Fatal("generate token err: ", err) } fmt.Println(testOKToken)