Golang: 인증 JWT 생성 #1

19335 단어 jwtgotutorial
그동안 간단한 응용 프로그램인 Golang을 배웠고, 마침내 완전한 응용 프로그램을 만들고 싶지만 Golang에서 어떻게 인증해야 하는지에 대한 질문에 사로잡혔습니다. node-js에서 golang으로 이동하기로 결정했을 때의 질문이기도 합니다.

폴더 구조 소개



이 자습서에서는 일반적으로 사용하는 폴더 구조를 사용합니다.
├───앱
├───도메인
├───개체
│ └───도우미
├───인터페이스
│ └───http
└───서비스
└───사용자
├───저장소
│ └───포스트그레스
└───유스케이스app : go 프로그램이 처음 실행될 때 실행될 main.package를 포함합니다.domain : 구조체 도메인을 포함합니다.entities/helper : 도우미 기능이 포함되어 있습니다.interface/http : 진고닉 라우팅 핸들러를 포함합니다.service : 서비스 포함usecase : 사용자 대신 유스케이스를 넣을 위치.repository : 저장소를 넣을 위치

이제 생성된 폴더에서 go mod init shellrean.com/auth를 실행합니다.

postgresql에서 테이블 생성



DBMS의 경우 이번에는 PostgreSQL을 사용하여 다음 사양의 사용자 이름으로 테이블을 생성합니다.
  • id (int) 자동 증분
  • 이름(문자열)
  • 이메일(문자열)
  • 비밀번호(문자열)
  • created_at(타임스탬프)
  • updated_at(타임스탬프)
    그런 다음 1명의 더미 사용자를 만들고 Bcrypt-Generator의 온라인 도구를 사용하여 bcrypt에서 자유롭게 암호를 생성하도록 합니다.
    ## 도메인 파일 생성user.go 파일을 만들고 domain 폴더에 저장하면 이 파일에 도메인 구조와 사용자 인터페이스를 만듭니다.

  • package domain
    
    import (
        "time"
        "context"    
    )
    
    type User struct {
        ID          int64       `json:"id"`
        Name        string      `json:"name" validate:"required"`
        Email       string      `json:"email" validate:"required,email"`
        Password    string      `json:"password" validate:"required,min=6"`
        CreatedAt   time.Time   `json:"created_at"`
        UpdatedAt   time.Time   `json:"updated_at"`
    }
    
    type UserUsecase interface {
        Authentication(ctx context.Context, ur DTOUserLoginRequest) (DTOTokenResponse, error)
        RefreshToken(ctx context.Context, ur DTOTokenResponse) (DTOTokenResponse, error)
    }
    type UserRepository interface {
        GetByID(ctx context.Context, id int64) (User, error)
        GetByEmail(ctx context.Context, email string) (User, error)
    }
    
    

    domain.go 파일을 생성한 다음 동일한 폴더에 저장합니다. 이 파일의 토큰에 대해 도메인을 생성합니다.

    package domain
    
    type TokenDetails struct {
        AccessToken     string
        RefreshToken            string
        AtExpires       int64
        RtExpires       int64
    }
    


    파일 저장소 만들기


    main_user.go 파일을 생성하고 services/user/repository/postgres 폴더에 저장하면 데이터베이스에서 데이터를 검색하는 저장소 기능을 생성합니다.

    package postgres
    
    import (
        "context"
        "database/sql"
    
        "shellrean.com/auth/domain"
    )
    
    type postgresUserRepository struct {
        Conn *sql.DB
    }
    
    func NewPostgresUserRepository(Conn *sql.DB) domain.UserRepository {
        return &postgresUserRepository{
            Conn,
        }
    }
    
    func (m *postgresUserRepository) GetByEmail(ctx context.Context, email string) (u domain.User, err error) {
        query := `SELECT id,name,email,password,created_at,updated_at
                FROM users WHERE email=$1`
    
        err = m.Conn.QueryRowContext(ctx, query,email).
                Scan(&u.ID,&t.Name,&t.Email,&t.Password,&t.CreatedAt,&t.UpdatedAt)
        if err != nil {
            return err
        }
    
        return
    }
    


    도우미 파일 만들기


    token.go 파일을 생성하고 entities /helper 폴더에 저장하면 JWT로 작업을 수행하는 도우미 함수를 생성합니다.

    package helper
    
    import (
        "strings"
        "time"
    
        "github.com/dgrijalva/jwt-go"
    
        "shellrean.com/auth/domain"
    )
    
    func GenerateTokenDetail(td *domain.TokenDetails) {
        td.AtExpires = time.Now().Add(time.Minute * 15).Unix()
        td.RtExpires = time.Now().Add(time.Hour * 24 * 7).Unix()
    }
    
    func CreateAccessToken(key string, user domain.User, td *domain.TokenDetails) (err error) {
        atClaims := jwt.MapClaims{}
        atClaims["authorized"] = true
        atClaims["user_id"] = user.ID
        atClaims["exp"] = td.AtExpires
    
        at := jwt.NewWithClaims(jwt.SigningMethodHS256, atClaims)
        td.AccessToken, err = at.SignedString([]byte(key))
        if err != nil {
            return domain.ErrSessDecode
        }
        return
    }
    
    func ExtractToken(bearer string) (res string) {
        str := strings.Split(bearer, " ")
        if len(str) == 2 {
            res = str[1]
            return
        }
        return
    }
    
    func VerifyToken(key string, tokenString string) (*jwt.Token, error) {
        token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
            if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
                return nil, domain.ErrSessDecode
            }
            return []byte(key), nil
        })
        if err != nil {
            return nil, domain.ErrSessVerifation
        }
        return token, nil
    }
    
    func TokenValid(token *jwt.Token) error {
        if _, ok := token.Claims.(jwt.Claims); !ok || !token.Valid {
            return domain.ErrSessInvalid
        }
        return nil
    }
    
    func ExtractTokenMetadata(token *jwt.Token) map[string]interface{}{
        claims, ok := token.Claims.(jwt.MapClaims)
        if ok && token.Valid {
            return claims
        }
        return nil
    }
    


    이 시점에서 2개의 도메인 파일, 1개의 저장소 파일 및 1개의 도우미 파일을 만들었습니다. 다음 기사에서 계속하겠습니다.

    좋은 웹페이지 즐겨찾기