gin
Gin 环境搭建 golang程序热加载
gin 路由设置
组路由设置
gin 中返回的数据
get post 以及动态路由 传值 ,get post 数据解析到结构体
JWT
在go中使用jwt
中间件
参数绑定
Binding tags
blog
本文档使用 MrDoc 发布
-
+
首页
在go中使用jwt
# 为什么使用jwt 在过去的web项目中,通常使用的是Cookie-Session的认证方式。大致的流程如下: 1. 用户使用用户名个密码登录,发送信息到服务器。 2. 服务器拿到用户的信息,后会生成一份保存用户信息的session数据和与之对应的cookie id,然后保存 session在在服务器,把对应的session id返回给用户并保存在浏览器。 3. 后续来自该用户的每次请求都会携带相应的cookie。 4. 服务端通过带来的cookie找到之前保存的与之对应的用户信息。 那么这种方式有什么弊端呢? * 不支持跨域 * 通过数据库查询响应的用户信息耗时 * CSRF攻击(跨站伪造请求) # 在gin 框架中使用jwt ## 安装包 ``` github.com/dgrijalva/jwt-go github.com/gin-gonic/gin ``` ## 生成TOKEN ``` type MyClaims struct { Username string `json:"username"` jwt.StandardClaims } // 定义过期时间 const TokenExpireDuration = time.Hour * 2 //定义secret var MySecret = []byte("这是一段用于生成token的密钥") //生成jwt func GenToken(username string) (string, error) { c := MyClaims{ username, jwt.StandardClaims{ ExpiresAt: time.Now().Add(TokenExpireDuration).Unix(), Issuer: "my-project", }, } //使用指定的签名方法创建签名对象 token := jwt.NewWithClaims(jwt.SigningMethodHS256, c) //使用指定的secret签名并获得完成的编码后的字符串token return token.SignedString(MySecret) } ``` ## 解析JWT ``` //解析JWT func ParseToken(tokenString string) (*MyClaims, error) { //解析token token, err := jwt.ParseWithClaims(tokenString, &MyClaims{}, func(token *jwt.Token) (i interface{}, err error) { return MySecret, nil }) if err != nil { return nil, err } if claims, ok := token.Claims.(*MyClaims); ok && token.Valid { return claims, nil } return nil, errors.New("invalid token") } ``` ## 编写 jwt 中间件 ``` //基于JWT认证中间件 func JWTAuthMiddleware() func(c *gin.Context) { return func(c *gin.Context) { authHeader := c.Request.Header.Get("Authorization") if authHeader == "" { c.JSON(http.StatusOK, gin.H{ "code": 2003, "msg": "请求头中的auth为空", }) c.Abort() return } parts := strings.SplitN(authHeader, " ", 2) if !(len(parts) == 2 && parts[0] == "Bearer") { c.JSON(http.StatusOK, gin.H{ "code": 2004, "msg": "请求头中的auth格式错误", }) //阻止调用后续的函数 c.Abort() return } mc, err := ParseToken(parts[1]) if err != nil { c.JSON(http.StatusOK, gin.H{ "code": 2005, "msg": "无效的token", }) c.Abort() return } //将当前请求的username信息保存到请求的上下文c上 c.Set("username", mc.Username) //后续的处理函数可以通过c.Get("username")来获取请求的用户信息 c.Next() } } ``` ## 注册一条理由/auth,获取token ``` type UserInfo struct { Username string `json:"username"` Password string `json:"password"` } func authHandler(c *gin.Context) { var user UserInfo err := c.ShouldBind(&user) if err != nil { c.JSON(http.StatusOK, gin.H{ "code": 2001, "msg": "无效的参数", }) return } if user.Username == "cyl" && user.Password == "123456" { //生成token tokenString, _ := GenToken(user.Username) c.JSON(http.StatusOK, gin.H{ "code": 200, "msg": "success", "data": gin.H{"token": tokenString}, }) return } c.JSON(http.StatusOK, gin.H{ "code": 2002, "msg": "鉴权失败", }) return } ``` ## 编写main函数进行测试 ``` package main import ( "errors" "github.com/dgrijalva/jwt-go" "github.com/gin-gonic/gin" "net/http" "strings" "time" ) func main() { r := gin.Default() r.POST("/auth", authHandler) r.GET("/home", JWTAuthMiddleware(), homeHandler) r.Run(":9000") } ``` 使用postman进行测试 首先访问一下http://localhost:9000/auth 返回结果如下: ![](/media/202303/2023-03-12_150550_9645890.017196638701655553.png) 接着我们继续注册一个需要token的路由/home ``` func homeHandler(c *gin.Context) { username := c.MustGet("username").(string) c.JSON(http.StatusOK, gin.H{ "code": 2000, "msg": "success", "data": gin.H{"username": username}, }) } ``` 访问http://localhost:9000/home 并且带上上次获取的token(记得加上Bearer) ![](/media/202303/2023-03-12_150634_1658790.08098590030887165.png) go中使用JWT就到此结束啦,附上原文连接:https://link.juejin.cn/?target=https%3A%2F%2Fwww.liwenzhou.com%2Fposts%2FGo%2Fjwt_in_gin%2F ## 全部代码 ``` package main import ( "errors" "fmt" "net/http" "strings" "time" "github.com/gin-gonic/gin" "github.com/golang-jwt/jwt/v4" ) type MyClaims struct { Username string `json:"username"` jwt.StandardClaims } // 定义过期时间 const TokenExpireDuration = time.Hour * 2 //const TokenExpireDuration = time.Second * 5 // 定义secret var MySecret = []byte("这是一段用于生成token的密钥") // 生成jwt func GenToken(username string) (string, error) { c := MyClaims{ username, jwt.StandardClaims{ ExpiresAt: time.Now().Add(TokenExpireDuration).Unix(), Issuer: "my-project", }, } //使用指定的签名方法创建签名对象 token := jwt.NewWithClaims(jwt.SigningMethodHS256, c) //使用指定的secret签名并获得完成的编码后的字符串token return token.SignedString(MySecret) } // 解析JWT func ParseToken(tokenString string) (*MyClaims, error) { //解析token token, err := jwt.ParseWithClaims(tokenString, &MyClaims{}, func(token *jwt.Token) (i interface{}, err error) { return MySecret, nil }) if err != nil { return nil, err } if claims, ok := token.Claims.(*MyClaims); ok && token.Valid { return claims, nil } return nil, errors.New("invalid token") } type UserInfo struct { Username string `form:"username" json:"username"` Password string `form:"password" json:"password"` } func authHandler(c *gin.Context) { var user UserInfo err := c.ShouldBind(&user) if err != nil { c.JSON(http.StatusOK, gin.H{ "code": 2001, "msg": "无效的参数", }) return } fmt.Println(user) if user.Username == "david" && user.Password == "123456" { //生成token tokenString, _ := GenToken(user.Username) c.JSON(http.StatusOK, gin.H{ "code": 200, "msg": "success", "data": gin.H{"token": tokenString}, }) return } c.JSON(http.StatusOK, gin.H{ "code": 2002, "msg": "鉴权失败", }) return } // gin主程序 func main() { r := gin.Default() r.GET("/user", JWTAuthMiddleware(), func(ctx *gin.Context) { ctx.JSON(http.StatusOK, gin.H{ "msg": "this is user page", }) }) r.POST("/auth", authHandler) r.Run() } // JWT 中间件 func JWTAuthMiddleware() gin.HandlerFunc { return func(ctx *gin.Context) { fmt.Println("jwt中间件") authHeader := ctx.Request.Header.Get("Authorization") if authHeader == "" { ctx.JSON(http.StatusOK, gin.H{ "code": 2003, "msg": "请求头的auth 为空", }) ctx.Abort() return } parts := strings.SplitN(authHeader, " ", 2) if !(len(parts) == 2 && parts[0] == "Bearer") { ctx.JSON(http.StatusOK, gin.H{ "code": 2004, "msg": "请求头中的auth格式错误", }) //阻止调用后续的函数 ctx.Abort() return } mc, err := ParseToken(parts[1]) if err != nil { ctx.JSON(http.StatusOK, gin.H{ "code": 2005, "msg": "无效的token", }) ctx.Abort() return } //将当前请求的username信息保存到请求的上下文c上 ctx.Set("username", mc.Username) //后续的处理函数可以通过c.Get("username")来获取请求的用户信息 ctx.Next() } } ```
admin
2023年3月12日 15:49
转发文档
收藏文档
上一篇
下一篇
手机扫码
复制链接
手机扫一扫转发分享
复制链接
Markdown文件
分享
链接
类型
密码
更新密码