Register เข้าสู่ระบบ ภาษา Go

Register ออกเสียงว่า เรจิสเตอร์ หรือ รีจิสเตอร์ หมายถึง การลงทะเบียน ซึ่งเราจะพบเห็นคำว่า Register ได้ จากเว็บไซต์ (Website) ต่าง ๆ ที่จัดกิจกรรม และต้องการให้ผู้ที่สนใจลงทะเบียน เพื่อร่วมกิจกรรมดังกล่าว หรือบางเว็บไซต์ที่เปิดรับสมาชิก เพื่อรับสิทธิประโยชน์ หรือรับส่วนลดต่าง ๆ ก็มักจะมีคำว่า Register ด้วยเช่นกัน คำว่า Register นี้ไม่จำเป็นต้องใช้บนคอมพิวเตอร์ (Computer) เพียงอย่างเดียว แต่สามารถพบเจอในชีวิตจริงทั่วไป เช่น ตอนที่เราสมัครเพื่อเข้ารับการอบรม หรือตอนที่เราสมัครสมาชิกใด ๆ


ข้อกำหนดเบื้องต้น


ข้อกำหนดสำหรับบทความนี้คือ คุณควรได้ทำตามบทความ Go Admin – โครงสร้างข้อมูล (struct) มาก่อน


เข้ารหัส password ด้วย bcrypt


bcrypt เป็น password hashing function ที่สร้างขึ้นจากพื้นฐานของ Blowfish cipher โดยการทำงานของ Blowfish cipher ที่การสร้าง key ใหม่ขึ้นมาจะต้องทำการ pre-processโดยใช้เวลาเทียบเก่ากับการเข้ารหัสตัวอักษรขนาด 4KB

ติดตั้ง bcrypt ด้วยคำสั่ง

go get golang.org/x/crypto/bcrypt
go mod tidy



การลงทะเบียน Register


เปิดไฟล์ authController.go เพิ่มโค้ด password, _ := bcrypt.GenerateFromPassword([]byte(data[“password”]), 14) และ แก้ไข Password: password, เพิ่ม database.DB.Create(&user)

package controllers

import (
	"go-admin/database"
	"go-admin/models"
	"github.com/gofiber/fiber/v2"
	"golang.org/x/crypto/bcrypt"
)

func Register(c *fiber.Ctx) error {
	var data map[string]string

	if err := c.BodyParser(&data); err != nil {
		return err
	}

	if data["password"] != data["password_confirm"] {
		c.Status(400)
		return c.JSON(fiber.Map{
			"massage": "password do not match",
		})

	}

	password, _ := bcrypt.GenerateFromPassword([]byte(data["password"]), 14)

	user := models.User{
		FirsName: data["first_name"],
		LastName: data["last_name"],
		Email: data["email"],
		Password: password,
	}

	database.DB.Create(&user)

	return c.JSON(user)
}

ไฟล์ user.go แก้ไข Password string เป็น Password []byte

package models

type User struct {
	Id       int
	FirsName string
	LastName string
	Email    string `gorm:"unique"`
	Password []byte
}

ไฟล์ connect.go เพิ่ม DB = database

package database

import (
	"go-admin/models"
	"gorm.io/driver/mysql"
	"gorm.io/gorm"
)

var DB *gorm.DB

func Connect() {

	dsn := "root:12345678@tcp(localhost:3306)/go_admin?charset=utf8&parseTime=True"
	database, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
	if err != nil {
		panic(err)
	}

	DB = database

	database.AutoMigrate(&models.User{})
}

ทดสอบการทำงาน


ที่ Postman คลิก Send ที่ด้านล่างแสดงข้อมูลและ Password ที่เข้ารหัสด้วย bcrypt


ดูข้อมูลที่เพิ่มเข้าไป


ที่ MYSQL -> go_admin – > คลิกขวาที่ตาราง users -> Select Top 1000 -> Results จะแสดงข้อมูลที่เพิ่มเข้าไป

ล็อกอิน เข้าสู่ระบบ


ล็อกอิน (Log in) คือการ เข้าสู่ระบบ เช่นเดียวกับคำว่า Sign in ซึ่งรูปแบบการเขียนของคำอาจจะไม่เหมือนกัน แต่เมื่อเอามาใช้ในระบบคอมพิวเตอร์ (Computer) แล้ว จะหมายถึงการเข้าสู่ระบบเช่นเดียวกัน เรามักจะพบเห็นคำเหล่านี้บนหน้าเว็บไซต์ (Website) หรือแอพพลิเคชั่น (Application) แบบต้องสมัครสมาชิกเพื่อใช้งาน โดยเมื่อสมัครสมาชิกเสร็จเรียบร้อยแล้ว สิ่งสำคัญที่ต้องใช้เข้าสู่ระบบก็คือ Username และ Password ซึ่งบางเว็บไซต์ หรือบางแอพพลิเคชั่น อาจออกแบบให้สามารถใช้ Email, เบอร์โทรศัพท์ หรือชื่อที่เราตั้งเอง ในการเข้าสู่ระบบได้อีกด้วย


ล็อกอิน (Log in)


ไฟล์ authController.go เพิ่มโค้ดดังนี้

package controllers

import (
	"go-admin/database"
	"go-admin/models"
	"github.com/gofiber/fiber/v2"
	"golang.org/x/crypto/bcrypt"
)

func Register(c *fiber.Ctx) error {
	var data map[string]string

	if err := c.BodyParser(&data); err != nil {
		return err
	}

	if data["password"] != data["password_confirm"] {
		c.Status(400)
		return c.JSON(fiber.Map{
			"massage": "password do not match",
		})

	}

	password, _ := bcrypt.GenerateFromPassword([]byte(data["password"]), 14)

	user := models.User{
		FirsName: data["first_name"],
		LastName: data["last_name"],
		Email: data["email"],
		Password: password,
	}

	database.DB.Create(&user)

	return c.JSON(user)
}

func Login(c *fiber.Ctx) error  {

	var data map[string]string

	if err := c.BodyParser(&data); err != nil {
		return err
	}

	var user models.User
	database.DB.Where("email = ?", data["email"]).First(&user)

	if user.Id == 0 {
		c.Status(404)
		return c.JSON(fiber.Map{
			"massage": "not found",
		})
	}

	if err := bcrypt.CompareHashAndPassword(user.Password, []byte(data["password"])); err != nil {
		c.Status(400)
		return c.JSON((fiber.Map{
			"massage": "incorrect password",
		}))
	}

	return c.JSON(user)

}

ไฟล์ routes.go เพิ่มเส้นทางใหม่ ด้วยโค้ด app.Post(“/api/login”, controllers.Login)

package routes

import (
	"go-admin/controllers"
	"github.com/gofiber/fiber/v2"
)

func Setup(app *fiber.App) {
	
	app.Post("/api/register", controllers.Register)
	app.Post("/api/login", controllers.Login)
}

ทดสอบการทำงาน


ที่ Postman เปิดแท็บใหม่ ใส่ http://localhost:8000/api/login

เลือกเป็น POST -> Body -> Raw -> JSON แล้วเขียนโค้ดด้านล่าง คลิก Send

{
    "email": "joth2022@doe.com",
    "password": "1"
}



ด้านล่างจะแสดงข้อความ “massage”: “not found” เพราะไม่มี email นี้ในระบบของเรา

ทดสอบ อีเมล มีในระบบ แต่ password ผิด -> Send

{
    "email": "joth@doe.com",
    "password": "12"
}


ด้านล่างจะแสดงข้อความ “massage”: “incorrect password” เพราะ password ไม่ถูกต้อง


ทดสอบ อีเมล มีในระบบ และ password ที่ถูกต้อง -> Send

{
    "email": "joth@doe.com",
    "password": "1"
}


ด้านล่างจะแสดงข้อมูลในระบบ


JWT


JWT ย่อมาจาก JSON Web Token เป็นรูปแบบหนึ่งที่ใช้ในการสร้างรหัส token จากข้อมูล JSON Data แล้วทำการเข้ารหัสด้วย Base64Url Encoded 


Token


เป็นรหัสชุดนึงที่เอาไว้สำหรับทดแทน session ซึ่งเอาไว้ระบุว่าคนๆนั้นคือใคร ตัวอย่างเช่น Facebook เมื่อล็อคอินเสร็จแล้วจะมี accessToken เพื่อระบุตัวตนว่าเป็นใคร ซึ่งตัว token เอามาใช้ในการทำ RESTFul API ทดแทนการทำ Web Server แบบเดิมๆ ที่เก็บในรูปแบบ session โดยตัว token จะถูกส่งไปทุกๆ request ผ่าน HTTP Headers


ติดตั้ง JWT

go get github.com/dgrijalva/jwt-go

ไฟล์ authController.go เพิ่มโค้ดดังนี้

package controllers

import (
	"go-admin/database"
	"go-admin/models"
	"strconv"
	"time"
	"github.com/dgrijalva/jwt-go"
	"github.com/gofiber/fiber/v2"
	"golang.org/x/crypto/bcrypt"
)

func Register(c *fiber.Ctx) error {
	var data map[string]string

	if err := c.BodyParser(&data); err != nil {
		return err
	}

	if data["password"] != data["password_confirm"] {
		c.Status(400)
		return c.JSON(fiber.Map{
			"massage": "password do not match",
		})

	}

	password, _ := bcrypt.GenerateFromPassword([]byte(data["password"]), 14)

	user := models.User{
		FirsName: data["first_name"],
		LastName: data["last_name"],
		Email: data["email"],
		Password: password,
	}

	database.DB.Create(&user)

	return c.JSON(user)
}

func Login(c *fiber.Ctx) error  {

	var data map[string]string

	if err := c.BodyParser(&data); err != nil {
		return err
	}

	var user models.User
	database.DB.Where("email = ?", data["email"]).First(&user)

	if user.Id == 0 {
		c.Status(404)
		return c.JSON(fiber.Map{
			"massage": "not found",
		})
	}

	if err := bcrypt.CompareHashAndPassword(user.Password, []byte(data["password"])); err != nil {
		c.Status(400)
		return c.JSON((fiber.Map{
			"massage": "incorrect password",
		}))
	}

	claims := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.StandardClaims{
		Issuer: strconv.Itoa(int(user.Id)), 
		ExpiresAt: time.Now().Add(time.Hour * 24).Unix(), // 1 day
	})

	token , err := claims.SignedString([]byte("secret"))

	if err != nil {
		return c.SendStatus(fiber.StatusInternalServerError)

	}

	return c.JSON(token)

}


ทดสอบการทำงาน


ที่ Postman ใส่ http://localhost:8000/api/login

เลือกเป็น POST -> Body -> Raw -> JSON แล้วเขียนโค้ดด้านล่าง คลิก Send

{
    "email": "joth@doe.com",
    "password": "1"
}


ที่ด้านล่างจะแสดง token

Leave a Reply

Your email address will not be published. Required fields are marked *