본문 바로가기
카테고리 없음

고 (Golang) | 이것만 알면 나도 개발 전문가 | Gin 을 이용한 CRUD RESTful API 개발 Part9 - JWT 인증 기능 추가하기 (4) - 회원가입 인증 처리 및 로그인 구현

by K-인사이터 2023. 3. 9.
반응형

이 포스팅은 쿠팡 파트너스 활동의 일환으로, 이에 따른 일정액의 수수료를 제공받습니다.

 

 

안녕하세요

오늘도 k-in 입니다.

 

1. 서론

"이것만 알면 나도 개발 전문가" 시리즈입니다.

"Gin 을 이용한 CRUD RESTful API 개발 Part9" 를 진행하겠습니다. 

 

Part9 에서는 회원가입 인증 처리 및 로그인 구현을 추가해보도록 하겠습니다. 

Part8 의 내용을 아직 학습하지 않았다면 아래의 링크로 접속하여 학습을 순서대로 이어가주세요. 

 

2023.03.07 - [Go] - 고 (Golang) | 이것만 알면 나도 개발 전문가 | Gin 을 이용한 CRUD RESTful API 개발 Part8 - JWT 인증 기능 추가하기 (3) - 회원가입 이메일 인증 로직 개발

 

고 (Golang) | 이것만 알면 나도 개발 전문가 | Gin 을 이용한 CRUD RESTful API 개발 Part8 - JWT 인증 기능

이 포스팅은 쿠팡 파트너스 활동의 일환으로, 이에 따른 일정액의 수수료를 제공받습니다. HTML 삽입 미리보기할 수 없는 소스 안녕하세요 오늘도 k-in 입니다. 1. 서론 "이것만 알면 나도 개발 전

k-in.tistory.com

 

 

이번 강좌를 진행하기 위해서는 Part8 의 소스코드가 꼭 필요합니다.

아래 웹 사이트에 방문하셔서 Clone 버튼을 꾹 눌러주세요.

 

 

 

https://bitbucket.org/kinstory/gin-tutorial/src/part8/

 

Bitbucket

 

bitbucket.org

 

2. 강좌 순서

이번 강좌에서는 회원가입 인증 처리 및 로그인 구현에 대해 다루겠습니다. 

순서는 아래와 같습니다.

  1. /verify/:verificationCode API 를 통해 인증 처리(회원 활성화)
  2. /login API 를 통해 사용자 로그인 구현

3. verify API 를 통해 인증 처리

지난 시간에 가입한 이메일로 인증 코드를 전송하는 방법을 배웠습니다. 

이번에는 사용자가 인증 코드를 통해 인증하는 API 를 구현합니다. 

 

Part8 소스코드 기준으로 아래의 두개의 파일만 수정하면 됩니다. 

  • controllers/auth.controller.go
  • routes/auth.routes.go
// filename: auth.routes.go
// 코드 생략 
func (arc *AuthRouteController) AuthRoute(rg *gin.RouterGroup) {
	router := rg.Group("/auth")
	router.POST("/register", arc.authController.SignUpUser)
    // (1)
	router.GET("/verify/:verificationCode", arc.authController.VerifyEmail)
}

 

 

  1. :verificationCode 는 URL 경로를 파라미터로 처리하는 기법(parameters in path)입니다. 예를 들어 "/verify/blah" 라고 url 이 주어지면 verificationCode 는 blah 가 됩니다. 컨트롤러 로직에서 이 파라미터를 어떻게 불러오는지 확인해볼까요? 
func (ac *AuthController) VerifyEmail(ctx *gin.Context) {
	// (1)
	code := ctx.Params.ByName("verificationCode")
	
    // (2)
	query := bson.D{{Key: "verificationCode", Value: code}}
    // (3) 
	update := bson.D{{
		Key: "$set",
		Value: bson.D{{
			Key:   "verified",
			Value: true,
		}}}, {
		Key: "$unset",
		Value: bson.D{{
			Key:   "verificationCode",
			Value: "",
		}},
	}}
	// (4)
	result, err := ac.collection.UpdateOne(ac.ctx, query, update)
	if err != nil {
		ctx.JSON(
			http.StatusForbidden,
			gin.H{"status": "fail", "message": err.Error()},
		)
		return
	}

	if result.MatchedCount == 0 {
		ctx.JSON(
			http.StatusForbidden,
			gin.H{"status": "fail", "message": "could not verify email address"},
		)
		return
	}

	ctx.JSON(
		http.StatusOK,
		gin.H{
			"status":  "success",
			"message": "email verified successfully",
		},
	)
}

 

  1. ctx.Params.ByName 함수를 통해 ":verficiationCode" 라고 지정한 path 변수를 가져올 수 있습니다. 불러올때는 verificationCode 라고 키 이름을 입력만해도 됩니다. 
  2. bson.D 타입은 Key-Value Pair 리스트입니다. 즉, bson.D{{"foo", "bar"}, {"hello", "world"}, {"pi", 3.14159}} 와 같은 여러개의 키-벨류 데이터를 한번에다룰 수 있는 자료구조입니다. 발급된 인증 코드의 조회를 위해 DB 칼럼의 이름과 사용자가 입력한 path 변수를 대입합니다. 
  3. 인증 코드와 일치하는 회원정보가 찾아진 경우 사용자를 인증 상태로 데이터를 갱신해야 합니다. 그리고 이미 발급된 인증 코드는 삭제해야 합니다. $set 를 통해 갱신하며 $unset 을 통해 삭제할 수 있습니다. $unset 의 경우 해당 문서에서 칼럼 자체를 삭제하는 것으로 Value 절에 empty value 를 입력합니다. 
  4. UpdateOne 함수를 사용해 조회된 데이터 중 하나의 데이터만 업데이트 처리하도록 합니다. 이렇게되면 모든 데이터가 update 되는 불상사를 막을 수 있습니다. 

코드 수정이 완료되었습니다. (짝짝짝)

 

이제 발송된 이메일 본문에 버튼을 클릭하여 인증 처리가 잘되는지 확인해봅시다. 

 

 

버튼을 클릭하게되면 아래와 같이 팝업이 열리면서 인증 처리가 잘되었다고 메시지를 출력합니다. 

데이터 베이스에도 의도대로 verified 칼럼이 true 로 변경된 것을 확인할 수 있습니다. 

 

4. login API 를 통해 사용자 로그인 처리 

 

로그인 API 를 통해 JWT 를 발급하는 과정을 처리해보겠습니다. 

아래와 그림 처럼 인증 정보를 전달 시 JWT 가 발급되도록 구현해보겠습니다. 

이 과정을 위해 아래의 준비 작업이 필요합니다. 

refresh 토큰을 통해 만료된 access_token 을 다시 발급받는 로직의 구현도 필요합니다. 

하지만 내용이 길기에 우선 로그인만 이번 시간에 구현해보고 만료된 access_token 을 갱신하는 방법은 다음 시간에 다루어보겠습니다. 

 

  1. 토큰 생성을 위해 RSA 키 생성 및 생성된 키를 인코딩(base64)
  2. 환경 변수 파일 및 Config 타입 수정
  3. JWT 구현을 위한 패키지 설치 
  4. 사용자 인증 정보 입력을 위한 SignInInput 타입 정의
  5. login API 를 위한 컨트롤러 구현
  6. 라우팅에 추가 

4.1. 토큰 생성을 위해 RSA 키 생성 및 생성된 키를 인코딩(base64)

JWT 는 데이터가 훤히 보이는 토큰입니다. 따라서, 저장하는 정보가 민감한 정보를 포함하지 않는지 신경써야 합니다. 

그리고 중요한 포인트는 JWT 서명 절차를 통해 변조가 불가능하도록 잘 보호해야 합니다. 

이를 위해 RSA 키가 필요합니다. RSA 키를 생성하는 방법은 여러가지가 있으나 이는 다음에 소개하도록 하며 

이번 시간에는 간단하게 RSA 키를 생성하는 방법을 소개해드리겠습니다. 

 

아래의 사이트는 RSA 키를 생성하는 온라인 도구입니다. 접속해볼까요? 

https://travistidwell.com/jsencrypt/demo/

 

Online RSA Key Generator

Text to encrypt: This is a test!   Encrypt / Decrypt Encrypted:

travistidwell.com

 

 

사이트에 접속하면 아래와 같이 "Generate New Keys" 버튼이 보입니다. 눌러주면 오른쪽 그림과 같이 Private/Public 키를 생성합니다. 강좌를 목적으로 하기 때문에 실제로 키를 생성 시에는 온라인 도구를 이용해서는 안됩니다. 데모용으로만 사용해주세요.

 

 

생성된 키는 환경변수에 적용하기는 어려운 형태로 보입니다. 아래의 온라인 도구에 접근하셔서 Base64 로 인코딩을 합니다. 

 

https://www.base64encode.org/

 

Base64 Encode and Decode - Online

Encode to Base64 format or decode from it with various advanced options. Our site has an easy to use online tool to convert your data.

www.base64encode.org

온라인 도구에 접근하셔서 Private Key, Public Key 를 순차적으로 인코딩하여 결과를 노트패드에 복사해둡니다. 

 

4.2. 환경 변수 파일 및 Config 타입 수정

base64 로 인코딩된 private/public 키를 환경 변수 파일에 저장해두고 이를 불러오도록 소스 수정이 필요합니다. 

Part8 의 소스코드를 불러와서 app.env 파일을 수정해봅시다. 

 

아래의 키들이 생성되었습니다. Refresh Token 과 Access Token 을 위한 Private/Public Key 는 각각 다르게 생성해야 합니다. 

즉, RSA Online Tool 에서 두번 발급 받아서 각각 Refresh 와 Access Token 에 할당해야합니다. 

 

  • ACCESS_TOKEN_PRIVATE_KEY 
  • ACCESS_TOKEN_PUBLIC_KEY
  • REFRESH_TOKEN_PRIVATE_KEY 
  • REFRESH_TOKEN_PUBLIC_KEY
HOST=http://localhost
PORT=8001
# Mongo & Redis Configuration
MONGO_INITDB_ROOT_USERNAME=root
MONGO_INITDB_ROOT_PASSWORD=password123
MONGODB_LOCAL_URI=mongodb://root:password123@localhost:6000
REDIS_URL=localhost:6379

EMAIL_FROM=k-in@k-in.com
SMTP_HOST=smtp.mailtrap.io
SMTP_PORT=587
SMTP_USER=<<username>>
SMTP_PASS=<<password>>

ACCESS_TOKEN_PRIVATE_KEY=LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlCUEFJQkFBSkJBTzVIKytVM0xrWC91SlRvRHhWN01CUURXSTdGU0l0VXNjbGFFKzlaUUg5Q2VpOGIxcUVmCnJxR0hSVDVWUis4c3UxVWtCUVpZTER3MnN3RTVWbjg5c0ZVQ0F3RUFBUUpCQUw4ZjRBMUlDSWEvQ2ZmdWR3TGMKNzRCdCtwOXg0TEZaZXMwdHdtV3Vha3hub3NaV0w4eVpSTUJpRmI4a25VL0hwb3piTnNxMmN1ZU9wKzVWdGRXNApiTlVDSVFENm9JdWxqcHdrZTFGY1VPaldnaXRQSjNnbFBma3NHVFBhdFYwYnJJVVI5d0loQVBOanJ1enB4ckhsCkUxRmJxeGtUNFZ5bWhCOU1HazU0Wk1jWnVjSmZOcjBUQWlFQWhML3UxOVZPdlVBWVd6Wjc3Y3JxMTdWSFBTcXoKUlhsZjd2TnJpdEg1ZGdjQ0lRRHR5QmFPdUxuNDlIOFIvZ2ZEZ1V1cjg3YWl5UHZ1YStxeEpXMzQrb0tFNXdJZwpQbG1KYXZsbW9jUG4rTkVRdGhLcTZuZFVYRGpXTTlTbktQQTVlUDZSUEs0PQotLS0tLUVORCBSU0EgUFJJVkFURSBLRVktLS0tLQ==

ACCESS_TOKEN_PUBLIC_KEY=LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUZ3d0RRWUpLb1pJaHZjTkFRRUJCUUFEU3dBd1NBSkJBTzVIKytVM0xrWC91SlRvRHhWN01CUURXSTdGU0l0VQpzY2xhRSs5WlFIOUNlaThiMXFFZnJxR0hSVDVWUis4c3UxVWtCUVpZTER3MnN3RTVWbjg5c0ZVQ0F3RUFBUT09Ci0tLS0tRU5EIFBVQkxJQyBLRVktLS0tLQ==
ACCESS_TOKEN_EXPIRED_IN=15m
ACCESS_TOKEN_MAXAGE=15


REFRESH_TOKEN_PRIVATE_KEY=LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlCT1FJQkFBSkJBSWFJcXZXeldCSndnYjR1SEhFQ01RdHFZMTI5b2F5RzVZMGlGcG51a0J1VHpRZVlQWkE4Cmx4OC9lTUh3Rys1MlJGR3VxMmE2N084d2s3TDR5dnY5dVY4Q0F3RUFBUUpBRUZ6aEJqOUk3LzAxR285N01CZUgKSlk5TUJLUEMzVHdQQVdwcSswL3p3UmE2ZkZtbXQ5NXNrN21qT3czRzNEZ3M5T2RTeWdsbTlVdndNWXh6SXFERAplUUloQVA5UStrMTBQbGxNd2ZJbDZtdjdTMFRYOGJDUlRaZVI1ZFZZb3FTeW40YmpBaUVBaHVUa2JtZ1NobFlZCnRyclNWZjN0QWZJcWNVUjZ3aDdMOXR5MVlvalZVRlVDSUhzOENlVHkwOWxrbkVTV0dvV09ZUEZVemhyc3Q2Z08KU3dKa2F2VFdKdndEQWlBdWhnVU8yeEFBaXZNdEdwUHVtb3hDam8zNjBMNXg4d012bWdGcEFYNW9uUUlnQzEvSwpNWG1heWtsaFRDeWtXRnpHMHBMWVdkNGRGdTI5M1M2ZUxJUlNIS009Ci0tLS0tRU5EIFJTQSBQUklWQVRFIEtFWS0tLS0t

REFRESH_TOKEN_PUBLIC_KEY=LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUZ3d0RRWUpLb1pJaHZjTkFRRUJCUUFEU3dBd1NBSkJBSWFJcXZXeldCSndnYjR1SEhFQ01RdHFZMTI5b2F5Rwo1WTBpRnBudWtCdVR6UWVZUFpBOGx4OC9lTUh3Rys1MlJGR3VxMmE2N084d2s3TDR5dnY5dVY4Q0F3RUFBUT09Ci0tLS0tRU5EIFBVQkxJQyBLRVktLS0tLQ==
REFRESH_TOKEN_EXPIRED_IN=60m
REFRESH_TOKEN_MAXAGE=60

 

  • config/default.go 소스코드에 환경변수 파일과 맵핑할 필드들을 Config 타입 내에 선언합니다. 
type Config struct {
	Host      string `mapstructure:"HOST"`
	Port      string `mapstructure:"PORT"`
	MongoUri  string `mapstructure:"MONGODB_LOCAL_URI"`
	RedisUri  string `mapstructure:"REDIS_URL"`
	EmailFrom string `mapstructure:"EMAIL_FROM"`
	SMTPPass  string `mapstructure:"SMTP_PASS"`
	SMTPUser  string `mapstructure:"SMTP_USER"`
	SMTPHost  string `mapstructure:"SMTP_HOST"`
	SMTPPort  int    `mapstructure:"SMTP_PORT"`

	AccessTokenPrivateKey  string        `mapstructure:"ACCESS_TOKEN_PRIVATE_KEY"`
	AccessTokenPublicKey   string        `mapstructure:"ACCESS_TOKEN_PUBLIC_KEY"`
	RefreshTokenPrivateKey string        `mapstructure:"REFRESH_TOKEN_PRIVATE_KEY"`
	RefreshTokenPublicKey  string        `mapstructure:"REFRESH_TOKEN_PUBLIC_KEY"`
	AccessTokenExpiresIn   time.Duration `mapstructure:"ACCESS_TOKEN_EXPIRED_IN"`
	RefreshTokenExpiresIn  time.Duration `mapstructure:"REFRESH_TOKEN_EXPIRED_IN"`
	AccessTokenMaxAge      int           `mapstructure:"ACCESS_TOKEN_MAXAGE"`
	RefreshTokenMaxAge     int           `mapstructure:"REFRESH_TOKEN_MAXAGE"`
}

 

4.3. JWT 구현을 위한 패키지 설치 

JWT 구현을 처음부터 끝까지 진행하는 것은 어리석은 일입니다. 아래와 같이 패키지를 간단히 설치하여 개발이 가능합니다. 

 

go get github.com/golang-jwt/jwt

패키지의 상세한 정보 및 취약점 등등의 정보는 아래를 통해서 확인해주세요.

 

https://pkg.go.dev/github.com/golang-jwt/jwt/v4

 

jwt package - github.com/golang-jwt/jwt/v4 - Go Packages

Example (atypical) using the RegisteredClaims type by itself to parse a token. The RegisteredClaims type is designed to be embedded into your custom types to provide standard validation features. You can use it alone, but there's no way to retrieve other f

pkg.go.dev

 

4.4. 사용자 인증 정보 입력을 위한 SignInInput 타입 정의

login API 는 로그인 처리를 위해 이메일과 패스워드 두개의 입력 값을 받아야 합니다. 

이를 위해 models/user.model.go 파일에 아래와 같이 SignInInput 타입을 정의합니다. 

이 모델 또한 json ↔ mongodb 에 교환되므로 Tag 를 지정합니다.

그리고 바인딩옵션에 "required" 라고 마킹하여 사용자가 모두 입력해야만 데이터가 유효하도록 정의합니다. 

type SignInInput struct {
	Email    string `json:"email" bson:"email" binding:"required"`
	Password string `json:"password" bson:"password" binding:"required"`
}

 

또한, 로그인 구현을 위해서는 해시화된 패스워드와 사용자가 입력한 패스워드를 비교하는 함수가 필요합니다. 

utils/password.go 파일에 아래와 같이 함수를 추가합니다. 

CompareHashAndPassword 함수는 해시된 패스워드와 평문 패스워드를 비교하도록 지원하는 함수입니다. 

func VerifyPassword(hashedPassword string, candidatePassword string) error {
	return bcrypt.CompareHashAndPassword([]byte(hashedPassword), []byte(candidatePassword))
}

그리고 로그인 API 는 JWT 를 생성해야 하므로 utils/token.go 파일을 생성하여 아래의 코드를 입력합니다. 

 

 

package utils

import (
	"encoding/base64"
	"fmt"
	"time"

	"github.com/golang-jwt/jwt"
)

func CreateToken(ttl time.Duration, payload interface{}, privateKey string) (string, error) {
    // (1) 
	decodedPrivateKey, err := base64.StdEncoding.DecodeString(privateKey)
	if err != nil {
		return "", fmt.Errorf("could not decode key: %w", err)
	}
	// (2) 
	key, err := jwt.ParseRSAPrivateKeyFromPEM(decodedPrivateKey)
	if err != nil {
		return "", fmt.Errorf("create: parse key: %w", err)
	}

	now := time.Now().UTC()
	// (3) 
	claims := make(jwt.MapClaims)
	claims["sub"] = payload
	claims["exp"] = now.Add(ttl).Unix()
	claims["iat"] = now.Unix()
	claims["nbf"] = now.Unix()
	// (4)
	token, err := jwt.NewWithClaims(jwt.SigningMethodRS256, claims).SignedString(key)

	if err != nil {
		return "", fmt.Errorf("create: sign token: %w", err)
	}

	return token, nil

}

 

  1. 환경 변수 파일을 불러와 base64 인코딩된 privateKey 를 인자로 입력합니다. 따라서 이를 디코딩하여 원래 데이터로 되돌립니다. 
  2. ParseRSAPrivateKeyFromPEM 함수는 PEM 포맷으로부터 Private Key 를 바이트 배열로 출력합니다. 
  3. Claim 을 정의합니다. 만료시간, 생성시간, JWT 내에 보관할 데이터(이 강좌에서는 사용자 아이디 정보를 보관합니다)를 선언합니다. 
  4. 생성한 Claim 을 RS256 방식으로 서명하는 절차를 진행합니다. 그리고 리턴된 token 데이터를 리턴하면 완성됩니다. 

 

4.5. login API 를 위한 컨트롤러 구현

login API 를 위한 컨트롤러를 구현해보겠습니다. 로직의 순서는 아래와 같습니다. 

 

  1. 사용자 입력값을 모델에 바인딩 (필수 값이 없다면 에러를 출력하고 리턴됩니다.) 
  2. 이메일 입력값에 대응하는 사용자 정보가 있는지 조회합니다. 
  3. 조회된 사용자 정보의 해시된 패스워드와 평문 패스워드 입력값을 비교합니다. 
  4. 정보가 모두 일치하면 조회된 사용자 정보의 ID 값을 이용해 CreateToken 을 두번 호출하여 access_token, refresh_token 을 각각 생성합니다.
  5. 생성한 토큰은 Set-Cookie 헤더 응답을 통해 쿠키로 내려주고 access_token 은 별도로 응답 메시지에 출력합니다. 
func (ac *AuthController) SignInUser(ctx *gin.Context) {
	var creds *models.SignInInput

	if err := ctx.ShouldBindJSON(&creds); err != nil {
		ctx.JSON(
			http.StatusBadRequest,
			gin.H{
				"status":  "fail",
				"message": err.Error(),
			},
		)
		return
	}

	user, err := ac.userService.FindUserByEmail(creds.Email)
	if err != nil {
		if err == mongo.ErrNoDocuments {
			ctx.JSON(
				http.StatusBadRequest,
				gin.H{
					"status":  "fail",
					"message": "invalid email or password",
				},
			)
			return
		}
		ctx.JSON(
			http.StatusBadRequest,
			gin.H{
				"status":  "fail",
				"message": err.Error(),
			},
		)
		return
	}

	if !user.Verified {
		ctx.JSON(
			http.StatusUnauthorized,
			gin.H{
				"status":  "fail",
				"message": "you are not verified, please verify your email to login",
			},
		)
		return
	}

	if err := utils.VerifyPassword(user.Password, creds.Password); err != nil {
		ctx.JSON(
			http.StatusBadRequest,
			gin.H{
				"status": "fail", "message": "invalid email or password",
			},
		)
		return
	}

	config, _ := config.LoadConfig(".")

	access_token, err := utils.CreateToken(
		config.AccessTokenExpiresIn,
		user.ID,
		config.AccessTokenPrivateKey,
	)
	if err != nil {
		ctx.JSON(
			http.StatusBadRequest,
			gin.H{"status": "fail", "message": err.Error()},
		)
		return
	}

	refresh_token, err := utils.CreateToken(
		config.RefreshTokenExpiresIn,
		user.ID,
		config.RefreshTokenPrivateKey,
	)
	if err != nil {
		ctx.JSON(
			http.StatusBadRequest,
			gin.H{"status": "fail", "message": err.Error()},
		)
		return
	}

	ctx.SetCookie(
		"access_token", access_token, config.AccessTokenMaxAge*60, "/", "localhost", false, true,
	)
	ctx.SetCookie(
		"refresh_token", refresh_token, config.RefreshTokenMaxAge*60, "/", "localhost", false, true,
	)
	ctx.SetCookie(
		"logged_in",
		"true",
		config.AccessTokenMaxAge*60,
		"/",
		"localhost",
		false,
		false,
	)
	ctx.JSON(
		http.StatusOK,
		gin.H{"status": "success", "access_token": access_token},
	)
}

 

 

4.6. 라우팅에 추가 

컨트롤러 작성이 완료되었습니다. 

이제 간단한 라우팅 등록 절차만 진행하면 끝입니다. 고생하셨습니다. 

 

  1. /login 경로를 지정합니다. 그리고 컨트롤러 함수를 맵핑합니다. 
func (arc *AuthRouteController) AuthRoute(rg *gin.RouterGroup) {
	router := rg.Group("/auth")
	router.POST("/register", arc.authController.SignUpUser)
	router.GET("/verify/:verificationCode", arc.authController.VerifyEmail)
    // (1) 
	router.POST("/login", arc.authController.SignInUser)
}

 

5. 테스트 

 

task dev 명령을 실행해 웹 어플리케이션을 실행하고 아래와 같이 여러가지 방법으로 테스트해봅니다.

 

postman 에 익숙하지 않다면 아래의 curl 명령어를 통해서도 테스트할 수 있습니다. 

 

curl --location 'http://localhost:8001/api/auth/login' \
--header 'Content-Type: application/json' \
--data-raw '{
    "email": "k-in4@k-in.com",
    "password": "test1234!"
}'

 

인증된 사용자 계정을 이용해 로그인을 하면 아래와 같이 access_token 이 출력됩니다.

만약, 이메일 인증을 하지 않은 사용자가 로그인하려고 하면 로그인 실패가 떨어집니다. 

맺음말 

오늘은 "Gin 을 이용한 CRUD RESTful API 개발 Part9" 의 "JWT 인증 기능 추가하기 (4)" 를 진행하였습니다. 

이 과정을 통해서 "회원 인증 처리 및 로그인 구현"을 하였습니다. 

그리고 아래의 스킬들을 학습하였습니다. 

 

  • MongoDB 칼럼 업데이트 및 칼럼 삭제
  • MongoDriver 의 UpdateOne 함수 사용법
  • RSA Private/Public Key 생성 방법
  • Base64 인코딩 방법 
  • Hash 패스워드와 평문 패스워드를 비교하는 방법 
  • JWT 토큰을 생성하는 방법
  • Set-Cookie 헤더 응답을 내려주는 방법 
  • 등등 

 

 

다음 시간에서는 "Refresh Token 을 이용한 Access Token 갱신"을 진행하도록 하겠습니다. 

 

오늘 진행한 튜토리얼의 소스는 아래의 경로에서 다운받아서 살펴볼 수 있습니다.

아래 웹 사이트에 방문하셔서 Clone 버튼을 꾹 눌러주세요. 

 

https://bitbucket.org/kinstory/gin-tutorial/src/part9/

 

Bitbucket

 

bitbucket.org

 

 

 

이상으로 K-IN 이었습니다.

즐거운 하루되세요 

 

반응형