Go, Gin, MySQL 및 Docker를 사용한 블로그 프로젝트 - 3부

우리는 블로그 apis를 생성하여 중단했습니다part 2. 이 기사에서는 User 구조체를 설계하고 User 인증을 구현합니다. 인증을 위해 JWT 토큰을 사용할 것입니다.

Part 2Part 1을 통과했는지 확인하십시오.

참고: 이 시리즈의 모든 코드는 여기repo에서 찾을 수 있습니다. 이 문서의 코드를 얻으려면 part-3를 통해 분기git checkout part-3를 확인하십시오.

내용물


  • 사용자 API 및 인증 생성
  • Designing user struct
  • Hash and compare hashed password
  • User register and login repositories
  • User register and login services
  • User register and login controllers
  • User register and login routes

  • Configure main.go
  • API demo
  • Wrap up

  • 사용자 API 및 인증 생성



    사용자 구조체 설계


    models 폴더 안에 아래 내용이 있는 user.go 파일을 추가합니다.

    package models
    import "time"
    
    //User -> User struct to save user on database
    type User struct {
        ID        int64     `gorm:primary_key;auto_increment;json:id`
        FirstName string    `json:"first_name"`
        LastName  string    `json:"last_name"`
        Email     string    `json:"email"`
        Password  string    `json:"password"`
        IsActive  bool      `json:"is_active"`
        CreatedAt time.Time `json:"created_at", omitempty`
        UpdatedAt time.Time `json:"updated_at", omitempty`
    }
    
    //TableName -> returns the table name of User Model
    func (user *User) TableName() string {
        return "user"
    }
    
    //UserLogin -> Request Binding for User Login
    type UserLogin struct {
        Email    string `form:"email" binding:"required"`
        Password string `form:"password" binding:"required"`
    }
    
    //UserRegister -> Request Binding for User Register
    type UserRegister struct {
        Email     string `form:"email" json:"email" binding:"required"`
        Password  string `form:"password" json:"password" binding:"required"`
        FirstName string `form:"first_name"`
        LastName  string `form:"last_name"`
    }
    
    //ResponseMap -> response map method of User
    func (user *User) ResponseMap() map[string]interface{} {
        resp := make(map[string]interface{})
        resp["id"] = user.ID
        resp["email"] = user.Email
        resp["first_name"] = user.FirstName
        resp["last_name"] = user.LastName
        resp["is_active"] = user.IsActive
        resp["created_at"] = user.CreatedAt
        resp["updated_at"] = user.UpdatedAt
        return resp
    }
    


    해시된 비밀번호 해시 및 비교



    보안을 위해 데이터베이스에서 해시된 암호를 암호화합니다. 명령줄에서 go get golang.org/x/crypto/bcrypt를 실행하여 bcrypt 패키지를 설치합니다. util/password.go 파일을 생성하고 다음 코드를 추가합니다.
    Note : A project directory structure is shown down below
    package util
    import "golang.org/x/crypto/bcrypt"
    
    func HashPassword(password string) (string, error) {
        bytes, err := bcrypt.GenerateFromPassword([]byte(password), 14)
        return string(bytes), err
    }
    
    func CheckPasswordHash(password string, hash string) error {
        err := bcrypt.CompareHashAndPassword([]byte(hash), []byte(password))
        return err
    }
    


    사용자 등록 및 로그인 저장소


    api/repository/user.go 파일을 추가하고 다음 코드를 추가합니다.

    package repository
    
    import (
        "blog/infrastructure"
        "blog/models"
        "blog/util"
    )
    
    //UserRepository -> UserRepository resposible for accessing database
    type UserRepository struct {
        db infrastructure.Database
    }
    
    //NewUserRepository -> creates a instance on UserRepository
    func NewUserRepository(db infrastructure.Database) UserRepository {
        return UserRepository{
            db: db,
        }
    }
    
    //CreateUser -> method for saving user to database
    func (u UserRepository) CreateUser(user models.UserRegister) error {
    
        var dbUser models.User
        dbUser.Email = user.Email
        dbUser.FirstName = user.FirstName
        dbUser.LastName = user.LastName
        dbUser.Password = user.Password
        dbUser.IsActive = true
        return u.db.DB.Create(&dbUser).Error
    }
    
    //LoginUser -> method for returning user
    func (u UserRepository) LoginUser(user models.UserLogin) (*models.User, error) {
    
        var dbUser models.User
        email := user.Email
        password := user.Password
    
        err := u.db.DB.Where("email = ?", email).First(&dbUser).Error
        if err != nil {
            return nil, err
        }
    
        hashErr := util.CheckPasswordHash(password, dbUser.Password)
        if hashErr != nil {
            return nil, hashErr
        }
        return &dbUser, nil
    }
    
    


    여기dbUser.IsActive = true 이것은 약간 잘못된 것 같습니까? 아니요, 일부러 지금입니다. 나중에 다가오는 부분에서 우리는 이메일을 통해 사용자 확인을 구현할 것입니다.

    사용자 등록 및 로그인 서비스


    api/service/user.go 파일을 추가하고 다음 코드를 추가합니다.

    package service
    import (
        "blog/api/repository"
        "blog/models"
    )
    
    //UserService UserService struct
    type UserService struct {
        repo repository.UserRepository
    }
    
    //NewUserService : get injected user repo
    func NewUserService(repo repository.UserRepository) UserService {
        return UserService{
            repo: repo,
        }
    }
    
    //Save -> saves users entity
    func (u UserService) CreateUser(user models.UserRegister) error {
        return u.repo.CreateUser(user)
    }
    
    //Login -> Gets validated user
    func (u UserService) LoginUser(user models.UserLogin) (*models.User, error) {
        return u.repo.LoginUser(user)
    
    }
    


    사용자 등록 및 로그인 컨트롤러


    jwt 패키지 설치go get github.com/golang-jwt/jwt 이 패키지는 JWT 토큰을 발행하는 데 사용됩니다. api/controller/user.go 파일을 추가하고 다음을 추가합니다.

    package controller
    
    import (
        "blog/api/service"
        "blog/models"
        "blog/util"
        "net/http"
        "time"
    
        "github.com/gin-gonic/gin"
        "github.com/golang-jwt/jwt"
    )
    
    //UserController struct
    type UserController struct {
        service service.UserService
    }
    
    //NewUserController : NewUserController
    func NewUserController(s service.UserService) UserController {
        return UserController{
            service: s,
        }
    }
    
    //CreateUser ->  calls CreateUser services for validated user
    func (u *UserController) CreateUser(c *gin.Context) {
        var user models.UserRegister
        if err := c.ShouldBind(&user); err != nil {
            util.ErrorJSON(c, http.StatusBadRequest, "Inavlid Json Provided")
            return
        }
    
        hashPassword, _ := util.HashPassword(user.Password)
        user.Password = hashPassword
    
        err := u.service.CreateUser(user)
        if err != nil {
            util.ErrorJSON(c, http.StatusBadRequest, "Failed to create user")
            return
        }
    
        util.SuccessJSON(c, http.StatusOK, "Successfully Created user")
    }
    
    //LoginUser : Generates JWT Token for validated user
    func (u *UserController) Login(c *gin.Context) {
        var user models.UserLogin
        var hmacSampleSecret []byte
        if err := c.ShouldBindJSON(&user); err != nil {
            util.ErrorJSON(c, http.StatusBadRequest, "Inavlid Json Provided")
            return
        }
        dbUser, err := u.service.LoginUser(user)
        if err != nil {
            util.ErrorJSON(c, http.StatusBadRequest, "Invalid Login Credentials")
            return
        }
        token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
            "user": dbUser,
            "exp":  time.Now().Add(time.Minute * 15).Unix(),
        })
    
        tokenString, err := token.SignedString(hmacSampleSecret)
        if err != nil {
            util.ErrorJSON(c, http.StatusBadRequest, "Failed to get token")
            return
        }
        response := &util.Response{
            Success: true,
            Message: "Token generated sucessfully",
            Data:    tokenString,
        }
        c.JSON(http.StatusOK, response)
    }
    
    


    사용자 등록 및 로그인 경로



    마지막으로 등록 및 로그인 api를 노출할 수 있는 부분에 도달했습니다.api/routes/user.go 에 다음 코드를 추가합니다.

    package routes
    
    import (
        "blog/api/controller"
        "blog/infrastructure"
    )
    
    //UserRoute -> Route for user module
    type UserRoute struct {
        Handler    infrastructure.GinRouter
        Controller controller.UserController
    }
    
    //NewUserRoute -> initializes new instance of UserRoute
    func NewUserRoute(
        controller controller.UserController,
        handler infrastructure.GinRouter,
    ) UserRoute {
        return UserRoute{
            Handler:    handler,
            Controller: controller,
        }
    }
    
    //Setup -> setups user routes
    func (u UserRoute) Setup() {
        user := u.Handler.Gin.Group("/auth")
        {
            user.POST("/register", u.Controller.CreateUser)
            user.POST("/login", u.Controller.LoginUser)
        }
    }
    


    위의 코드는 사용자 인증 경로registerlogin를 구성합니다.

    main.go 구성



    이제 마지막 코드는 main.go 파일을 업데이트하여 모든 것을 하나로 묶는 것입니다.

    package main
    import (
        "blog/api/controller"
        "blog/api/repository"
        "blog/api/routes"
        "blog/api/service"
        "blog/infrastructure"
        "blog/models"
    )
    
    func init() {
        infrastructure.LoadEnv()
    }
    
    func main() {
    
        router := infrastructure.NewGinRouter()
        db := infrastructure.NewDatabase()
    
        postRepository := repository.NewPostRepository(db)
        postService := service.NewPostService(postRepository)
        postController := controller.NewPostController(postService)
        postRoute := routes.NewPostRoute(postController, router)
        postRoute.Setup()
    
        // add up these 
        userRepository := repository.NewUserRepository(db)
        userService := service.NewUserService(userRepository)
        userController := controller.NewUserController(userService)
        userRoute := routes.NewUserRoute(userController, router)
        userRoute.Setup()
    
        db.DB.AutoMigrate(&models.Post{}, &models.User{})
    
        router.Gin.Run(":8000")
    }
    


    API 데모



    이제 서버를 가동하고 registerlogin api를 테스트할 시간입니다.

    docker-compose up --build
    


    이제 좋아하는 API 테스터 애플리케이션을 불러옵니다. 나는 사용할 것이다 postman

    등록 엔드포인트 테스트 -> /auth/register





    로그인 끝점 테스트 -> /auth/login





    jwt.io에서 토큰 확인





    마무리



    다음으로 예정된 파트 4에서는 다음 내용을 다룹니다.
  • 블로그에 사용자 추가
  • 관련 사용자 게시물의 API
  • 외 다수

  • 중요 링크:


  • 2부 링크: Blog with Go, Gin, MySQL and Docker Part 2
  • 1부 링크: Blog with Go, Gin, MySQL and Docker Part 1
  • 저장소 링크: Github
  • 클린 아키텍처에 대해 읽어보기: Clean Architecture

  • 정말 감사합니다! 피드백은 정말 감사합니다. 나는 . 연결합시다.

    즐거운 독서!!!

    좋은 웹페이지 즐겨찾기