1. 介绍
JWT
全称JSON Web Token
是一种跨域认证解决方案,属于一个开放的标准,它规定了一种Token
实现方式,目前多用于前后端分离项目和OAuth2.0
业务场景下。
jwt-go 是使用Go
语言实现的Json web token (JWT)
,目前GitHub Start 9.8k
,源码地址: https://github.com/dgrijalva/jwt-go,从版本3.2.1开始,源码地址变更为: github.com/golang-jwt/jwt
,需要下载最新版本时,可以使用这个地址。
1.2 集成示意图
2. 配置
2.1 编辑主配置
文件位置:./config.yaml
app: ... log: ... mysql: ... jwt: secret: liuqh.icu issuer: 猿码记 expire: 3h
|
2.2 新增结构体
文件位置: ./config/jwt.go
package config
import "time"
type jwt struct { Secret string `yaml:"secret"` Issuer string `yaml:"issuer"` Expire time.Duration `yaml:"expire"` }
|
3. 编辑中间件
文件位置:./middleware/jwt.go
,功能包括有中间件函数/创建Token/解析Token
3.1 中间件函数
package middleware
import ( "52lu/go-import-template/global" "52lu/go-import-template/model/dao" "52lu/go-import-template/model/request" "52lu/go-import-template/model/response" "errors" "github.com/gin-gonic/gin" "github.com/golang-jwt/jwt" "go.uber.org/zap" "net/http" "time" )
func JWTAuthMiddleware() func(ctx *gin.Context) { return func(ctx *gin.Context) { token := getToken(ctx) global.GvaLogger.Sugar().Infof("token: %s",token) if token == "" { response.Error(ctx,"Token不能为空!") ctx.Abort() return } userClaim, err := ParseToken(token) if err != nil { response.ErrorWithToken(ctx,"Token error :" + err.Error()) ctx.Abort() return } setContextData(ctx,userClaim,token) ctx.Next() } }
func setContextData(ctx *gin.Context,userClaim *request.UserClaims,token string) { userDao := &dao.UserDao{ Uid: userClaim.Uid, } user, err := userDao.FindUser() if err != nil { response.Error(ctx,"用户不存在!") ctx.Abort() return } user.Token = token ctx.Set("userClaim",userClaim) ctx.Set("user",user) }
func getToken(ctx *gin.Context) string { var token string token = ctx.Request.Header.Get("TOKEN") if token != "" { return token } if ctx.Request.Method == http.MethodGet { token, ok := ctx.GetQuery("token") if ok { return token } } if ctx.Request.Method == http.MethodPost { postParam := make(map[string]interface{}) _ = ctx.ShouldBindJSON(&postParam) token, ok := postParam["token"] if ok { return token.(string) } } return "" }
|
3.2 创建Token
func CreateToken(uid uint) (string, error) { newWithClaims := jwt.NewWithClaims(jwt.SigningMethodHS256, &request.UserClaims{ StandardClaims: &jwt.StandardClaims{ ExpiresAt: time.Now().Add(global.GvaConfig.Jwt.Expire).Unix(), Issuer: global.GvaConfig.Jwt.Issuer, IssuedAt: time.Now().Unix(), }, Uid: uid, }) return newWithClaims.SignedString([]byte(global.GvaConfig.Jwt.Secret)) }
|
3.3 解析Token
func ParseToken(tokenString string) (*request.UserClaims, error) { var err error var token *jwt.Token token, err = jwt.ParseWithClaims(tokenString, &request.UserClaims{}, func(token *jwt.Token) (interface{}, error) { return []byte(global.GvaConfig.Jwt.Secret), nil }) if err != nil { global.GvaLogger.Error("解析JWT失败", zap.String("error", err.Error())) return nil, err } userClaims, ok := token.Claims.(*request.UserClaims) if !ok || !token.Valid { return nil, errors.New("JWT验证失败") } return userClaims, nil }
|
4. 注册路由
4.1 不需要登录路由
1. 注册路由
文件位置:router/user_router.go
func InitUserRouter(engine *gin.Engine) { noLoginGroup := engine.Group("v1/user") { noLoginGroup.POST("login", v1.Login) } }
|
2. 路由绑定函数
文件位置:./api/v1/user_api.go
func Login(ctx *gin.Context) { var loginParam request.LoginParam _ = ctx.ShouldBindJSON(&loginParam) token, err := middleware.CreateToken(userRecord.ID) if err != nil { global.GvaLogger.Sugar().Errorf("登录失败,Token生成异常:%s", err) response.Error(ctx, "登录失败,账号或者密码错误!") return } userRecord.Token = token response.OkWithData(ctx, userRecord) }
|
3. 请求返回
4.2 需要登录路由
1. 注册路由
文件位置:router/user_router.go
func InitUserRouter(engine *gin.Engine) { ... tokenGroup := engine.Group("v1/user").Use(middleware.JWTAuthMiddleware()) { tokenGroup.POST("/detail", v1.GetUser) } }
|
2. 路由绑定函数
文件位置:./api/v1/user_api.go
func GetUser(ctx *gin.Context) { user, _ := ctx.Get("user") response.OkWithData(ctx, user) return }
|
3. 请求返回
关注微信公众号【猿码记】,回复【gin集成】获取源码。