简介

JWT是json web token
具体jwt的组成,加密方式等等自行百度解决,我这里仅写实现案例:


控制器代码

package controllerimport (    "errors"    "fmt"    "gindemo/dto"    "gindemo/middleware"    "gindemo/middleware/jwt"    "gindemo/models"    "github.com/gin-gonic/gin"    "log")func Login(c *gin.Context) {    loginInput := &dto.LoginInput{}//我这里分层了,主要是把参数验证这块单独分离出来了    if err := loginInput.BindingValidParams(c); err != nil {        middleware.ResponseError(c, 2001, err)        return    }    user := &models.User{}    fmt.Println(loginInput)    token, err := user.Login(loginInput.UserName, loginInput.Password)    if err != nil {        if err.Error() == "record not found" {            middleware.ResponseError(c, 500, errors.New("该用户不存在"))            return        } else {            middleware.ResponseError(c, 500, errors.New("登录错误"))            return        }    }    middleware.ResponseSuccess(c,token)    return}//用于测试使用func UserList(c *gin.Context) {    var user models.User    claims := c.MustGet("claims").(*jwt.CustomClaims)    users, err := user.ListUsers(claims.Name)    if err != nil {        log.Fatal(err)    }    middleware.ResponseSuccess(c, users)}

参数验证

package dtoimport (    "errors"    "gindemo/public"    "github.com/gin-gonic/gin"    ut "github.com/go-playground/universal-translator"    "gopkg.in/go-playground/validator.v9"    zh_translations "gopkg.in/go-playground/validator.v9/translations/zh"    "strings")type LoginInput struct {    UserName string `form:"username" validate:"required"`    Password string `form:"password" validate:"required"`}func (o *LoginInput) BindingValidParams(c *gin.Context) error {    //绑定数据    if err := c.ShouldBind(o); err != nil {        return err    }    v := c.Value("trans")    trans, ok := v.(ut.Translator)    if !ok {        trans, _ = public.Uni.GetTranslator("zh")    }    ////验证器注册翻译器    //e := zh_translations.RegisterDefaultTranslations(public.Validate, trans)    //if e != nil {    //    return e    //}    //验证    err := public.Validate.Struct(o)    if err != nil {        sliceErrs := []string{}        for _, e := range err.(validator.ValidationErrors) {            sliceErrs = append(sliceErrs, e.Translate(trans))        }        return errors.New(strings.Join(sliceErrs, ","))    }    return nil}

生成token

package modelsimport (   "errors" "fmt" "gindemo/database" "gindemo/middleware/jwt"  jwtgo "github.com/dgrijalva/jwt-go"   "github.com/jinzhu/gorm" "log" "time")type User struct {   Id int `form:"id" json:"id" gorm:"PRIMARY_KEY"`  Name string `form:"username" json:"username"`  Email string `form:"email" json:"email",binding:"required"`  Password string `form:"password" json:"-",binding:"required"`}type LoginResult struct {   User interface{} `json:"user"`  Token string `json:"token"`}func (User) TableName() string {   return "users"}func (u *User) Login(name string, password string) (token LoginResult, err error) {   var user User  fmt.Println(name, password)   obj := database.GormPool.Where("name = ? and password=?", name, password).First(&user)   if err = obj.Error; err != nil {      return  }   generateToken := GenerateToken(user)   return generateToken, nil}//测试使用func (u *User) ListUsers(name string) (users []User, err error) {   query := database.GormPool  if name != "" {      query = query.Where("name=?", name)   }   err = query.Find(&users).Error  if err != nil && err != gorm.ErrRecordNotFound {      return nil, err  }   return}// 生成令牌  创建jwt风格的tokenfunc GenerateToken(user User) LoginResult {   j := &jwt.JWT{      []byte("newtrekWang"),   }   claims := jwt.CustomClaims{      user.Id,      user.Name,      user.Password,      jwtgo.StandardClaims{         NotBefore: int64(time.Now().Unix() - 1000), // 签名生效时间  ExpiresAt: int64(time.Now().Unix() + 3600), // 过期时间 一小时  Issuer:    "cfun",                   //签名的发行者  },   }   token, err := j.CreateToken(claims)   if err != nil {      return LoginResult{         User:  user,         Token: token,      }   }   log.Println(token)   data := LoginResult{      User:  user,      Token: token,   }   return data}

创建jwt.go文件

package jwtimport (    "errors"    "fmt"    "gindemo/middleware"    "github.com/dgrijalva/jwt-go"    "github.com/gin-gonic/gin"    "log"    "time")// JWTAuth 中间件,检查tokenfunc JWTAuth() gin.HandlerFunc {    return func(c *gin.Context) {        token := c.Request.Header.Get("token")        if token == "" {            middleware.ResponseError(c, -1, errors.New("请求未携带token,无权限访问"))            c.Abort()            return        }        log.Print("get token: ", token)        j := NewJWT()        // parseToken 解析token包含的信息        claims, err := j.ParseToken(token)        fmt.Println("claims", claims)        if err != nil {            if err == TokenExpired {                middleware.ResponseError(c, -1, errors.New("授权已过期"))                c.Abort()                return            }            middleware.ResponseError(c, -1, err)            c.Abort()            return        }        // 继续交由下一个路由处理,并将解析出的信息传递下去        c.Set("claims", claims)    }}// JWT 签名结构type JWT struct {    SigningKey []byte}// 一些常量var (    TokenExpired     error  = errors.New("Token is expired")    TokenNotValidYet error  = errors.New("Token not active yet")    TokenMalformed   error  = errors.New("That's not even a token")    TokenInvalid     error  = errors.New("Couldn't handle this token:")    SignKey          string = "cfun")// 载荷,可以加一些自己需要的信息type CustomClaims struct {    ID       int    `json:"userId"`    Name     string `json:"name"`    Password string `json:"telephone"`    jwt.StandardClaims}// 新建一个jwt实例func NewJWT() *JWT {    return &JWT{        []byte(GetSignKey()),    }}// 获取signKeyfunc GetSignKey() string {    return SignKey}// 这是SignKeyfunc SetSignKey(key string) string {    SignKey = key    return SignKey}// CreateToken 生成一个tokenfunc (j *JWT) CreateToken(claims CustomClaims) (string, error) {    token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)    return token.SignedString(j.SigningKey)}// 解析Toknefunc (j *JWT) ParseToken(tokenString string) (*CustomClaims, error) {    token, err := jwt.ParseWithClaims(tokenString, &CustomClaims{}, func(token *jwt.Token) (interface{}, error) {        return j.SigningKey, nil    })    if err != nil {        if ve, ok := err.(*jwt.ValidationError); ok {            if ve.Errors&jwt.ValidationErrorMalformed != 0 {                return nil, TokenMalformed            } else if ve.Errors&jwt.ValidationErrorExpired != 0 {                // Token is expired                return nil, TokenExpired            } else if ve.Errors&jwt.ValidationErrorNotValidYet != 0 {                return nil, TokenNotValidYet            } else {                return nil, TokenInvalid            }        }    }    if claims, ok := token.Claims.(*CustomClaims); ok && token.Valid {        return claims, nil    }    return nil, TokenInvalid}// 更新tokenfunc (j *JWT) RefreshToken(tokenString string) (string, error) {    jwt.TimeFunc = func() time.Time {        return time.Unix(0, 0)    }    token, err := jwt.ParseWithClaims(tokenString, &CustomClaims{}, func(token *jwt.Token) (interface{}, error) {        return j.SigningKey, nil    })    if err != nil {        return "", err    }    if claims, ok := token.Claims.(*CustomClaims); ok && token.Valid {        jwt.TimeFunc = time.Now        claims.StandardClaims.ExpiresAt = time.Now().Add(1 * time.Hour).Unix()        return j.CreateToken(*claims)    }    return "", TokenInvalid}

用到的公共返回方法

package middlewareimport (    "encoding/json"    "github.com/gin-gonic/gin")const (    SuccessCode ResponseCode = 200)type ResponseCode inttype Response struct {    ErrorCode ResponseCode `json:"errno"`    ErrorMsg  string       `json:"errmsg"`    Data      interface{}  `json:"data"`}func ResponseError(c *gin.Context, code ResponseCode, err error) {    resp := &Response{ErrorCode: code, ErrorMsg: err.Error(), Data: ""}    c.JSON(200, resp)    response, _ := json.Marshal(resp)    c.Set("response", string(response))    //c.AbortWithError(200, err)}func ResponseSuccess(c *gin.Context, data interface{}) {    resp := &Response{ErrorCode: SuccessCode, ErrorMsg: "", Data: data}    c.JSON(200, resp)    response, _ := json.Marshal(resp)    c.Set("response", string(response))}

路由定义

func InitRouter() *gin.Engine {   file, _ := os.Create("logs/app/log")   gin.DefaultWriter = io.MultiWriter(file)   router := gin.Default()   router.Use(gin.Recovery(), gin.Logger())   //登录注册  router.POST("/login", controller.Login)   router.POST("/register", controller.Register)   //用户相关  userRoute := router.Group("user")   userRoute.Use(jwt.JWTAuth())//这里使用Use,jwtAuth就行   userRoute.GET("/list", controller.UserList)}

登录获取token.
(localhost:8080/login?username=cfun&password=123456)

测试token数据合法性
(localhost:8080/user/list).token放到header里面

里面需要密码未加密需要优化,也欢迎同学们指出其他错误,非常感谢,一起成长!!