faker-douyin-5. 用户登陆以及Jwt中间件实现

99 阅读2分钟

1. 输入输出定义

model/dto/request/user.go以及model/dto/response/user.go文件中分别添加以下代码

type UserLoginReq struct {
   Name     string `json:"name,omitempty" binding:"required"`
   Password string `json:"password,omitempty" binding:"required"`
}
type UserLoginSuccessRes struct {
   Id    uint64 `json:"id"`
   Token string `json:"token"`
}

2. 添加控制器处理函数

api/v1/userController.go中添加登陆处理函数

// Login POST douyin/v1/user/login/ 用户登陆
func Login(c *gin.Context) {
   var userLoginReq request.UserLoginReq
   err := c.ShouldBindJSON(&userLoginReq)
   if err != nil {
      common.FailWithMessage(err.Error(), c)
      return
   }
   usi := service.UserServiceImpl{}
   u := usi.GetTableUserByUsername(userLoginReq.Name)
   if utils.EnCoder(userLoginReq.Password) == u.Password {
      token := utils.GenerateToken(&u)
      common.OkWithDetailed(response.UserLoginSuccessRes{Id: uint64(u.ID), Token: token}, "登陆成功", c)
      return
   } else {
      common.FailWithMessage("username or password error", c)
   }
}

3. 注册路由

router/router.go文件中注册登陆路由

apiRouter.POST("/user/login/", v1.Login)

4. Jwt中间件

用户注册与用户登陆是不需要身份认证的,但是之后的发布视频、评论、关注等功能需要登陆才能操作。需要新增一个身份校验的中间件。先在utils/jwt.go文件中添加解析token的函数

var (
   TokenExpired     = errors.New("token is expired")
   TokenNotValidYet = errors.New("token not active yet")
   TokenMalformed   = errors.New("that's not even a token")
   TokenInvalid     = errors.New("couldn't handle this token")
)

// ParseToken 从token中解析出StandardClaims
func ParseToken(token string) (*jwt.StandardClaims, error) {
   jwtToken, err := jwt.ParseWithClaims(token, &jwt.StandardClaims{}, func(token *jwt.Token) (i interface{}, e error) {
      return []byte(global.Config.Jwt.Secret), 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 jwtToken != nil {
      if claims, ok := jwtToken.Claims.(*jwt.StandardClaims); ok && jwtToken.Valid {
         return claims, nil
      }
      return nil, TokenInvalid

   } else {
      return nil, TokenInvalid
   }
}

middleware目录下新建jwt.go文件

package jwt

import (
   "errors"
   "faker-douyin/model/common"
   "faker-douyin/utils"
   "github.com/gin-gonic/gin"
)

func Auth() gin.HandlerFunc {
   return func(context *gin.Context) {
      auth := context.Request.Header.Get("Authorization")
      // 未携带token
      if len(auth) == 0 {
         common.FailWithMessage("Unauthorized", context)
         context.Abort()
         return
      }
      Claims, err := utils.ParseToken(auth)
      if err != nil {
         // token过期
         if errors.Is(utils.TokenExpired, err) {
            common.FailWithMessage("token expired", context)
            context.Abort()
            return
         }
         // token无效
         common.FailWithMessage("Token Error", context)
         context.Abort()
         return
      }
      // 解析成功,将userId放入上下文(其实应该将Claims放入上下文,之后Claims会存放更多东西,角色,rbac权限等)
      context.Set("userId", Claims.Id)
      context.Next()
   }
}

5. 测试用户注册

WechatIMG54.jpeg

WechatIMG55.jpeg

6. 待提高

  • 基于gitflow的开发:目前都在master分支上开发,后续会在feature分支进行开发merge
  • 没有单元测试:没有学习好的单元测试