After Finish User Register and Login Service via MVC arch

This commit is contained in:
2025-07-19 23:55:25 +08:00
parent 56c625ee6b
commit edd693200c
12 changed files with 168 additions and 19 deletions

1
.idea/.name generated Normal file
View File

@@ -0,0 +1 @@
GinTutorial

2
.idea/modules.xml generated
View File

@@ -2,7 +2,7 @@
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/GinWithGormTutorial.iml" filepath="$PROJECT_DIR$/.idea/GinWithGormTutorial.iml" />
<module fileurl="file://$PROJECT_DIR$/.idea/GinTutorial.iml" filepath="$PROJECT_DIR$/.idea/GinTutorial.iml" />
</modules>
</component>
</project>

View File

@@ -19,6 +19,9 @@ type Config struct {
Name string
Charset string
}
Bcrypt struct {
Cost int
}
}
var AppConfig *Config

View File

@@ -1,7 +1,7 @@
package config
import (
"GinWithGormTutorial/global"
"GinTutorial/global"
"fmt"
"log"
"time"
@@ -11,7 +11,7 @@ import (
)
func buildDSN(user, password, host, port, db, charset string) string {
template := "%s:%s@tcp(%s:%s)/%s?charset=%s"
template := "%s:%s@tcp(%s:%s)/%s?charset=%s&parseTime=true"
return fmt.Sprintf(template, user, password, host, port, db, charset)
}

View File

@@ -0,0 +1,102 @@
package controllers
import (
"GinTutorial/global"
"GinTutorial/models"
"GinTutorial/utils"
"net/http"
"strconv"
"github.com/gin-gonic/gin"
)
func Register(ctx *gin.Context) {
var user models.User
if err := ctx.ShouldBindJSON(&user); err != nil {
ctx.JSON(http.StatusBadRequest, gin.H{
"error": err.Error(),
})
return
}
hashPassword, err := utils.HashPassword(user.Password)
if err != nil {
ctx.JSON(http.StatusInternalServerError, gin.H{
"error": err.Error(),
})
}
user.Password = hashPassword
if err := global.Db.AutoMigrate(&user); err != nil {
ctx.JSON(http.StatusInternalServerError, gin.H{
"error": err.Error(),
})
return
}
if err := global.Db.Create(&user).Error; err != nil {
ctx.JSON(http.StatusInternalServerError, gin.H{
"error": err.Error(),
})
return
}
token, err := utils.GenerateJWT(strconv.Itoa(int(user.ID)))
if err != nil {
ctx.JSON(http.StatusInternalServerError, gin.H{
"error": err.Error(),
})
return
}
ctx.JSON(http.StatusOK, gin.H{
"token": token,
})
}
func Login(ctx *gin.Context) {
var InputUser struct {
Username string `json:"username" binding:"required"`
Password string `json:"password" binding:"required"`
}
if err := ctx.ShouldBindJSON(&InputUser); err != nil {
ctx.JSON(http.StatusBadRequest, gin.H{
"error": err.Error(),
})
return
}
var user models.User
if err := global.Db.Where("username = ?", InputUser.Username).First(&user).Error; err != nil {
ctx.JSON(http.StatusUnauthorized, gin.H{
"error": "Invalid username or password",
})
return
}
if !utils.CheckPassword(InputUser.Password, user.Password) {
ctx.JSON(http.StatusUnauthorized, gin.H{
"error": "Invalid username or password",
})
return
}
token, err := utils.GenerateJWT(strconv.Itoa(int(user.ID)))
if err != nil {
ctx.JSON(http.StatusInternalServerError, gin.H{
"error": err.Error(),
})
return
}
ctx.JSON(http.StatusOK, gin.H{
"token": token,
})
}

7
go.mod
View File

@@ -1,10 +1,12 @@
module GinWithGormTutorial
module GinTutorial
go 1.24
require (
github.com/gin-gonic/gin v1.10.1
github.com/golang-jwt/jwt/v5 v5.2.3
github.com/spf13/viper v1.20.1
golang.org/x/crypto v0.40.0
gorm.io/driver/mysql v1.6.0
gorm.io/gorm v1.30.0
)
@@ -45,8 +47,7 @@ require (
go.uber.org/atomic v1.9.0 // indirect
go.uber.org/multierr v1.9.0 // indirect
golang.org/x/arch v0.8.0 // indirect
golang.org/x/crypto v0.32.0 // indirect
golang.org/x/net v0.33.0 // indirect
golang.org/x/net v0.41.0 // indirect
golang.org/x/sys v0.34.0 // indirect
golang.org/x/text v0.27.0 // indirect
google.golang.org/protobuf v1.36.1 // indirect

10
go.sum
View File

@@ -35,6 +35,8 @@ github.com/go-viper/mapstructure/v2 v2.2.1 h1:ZAaOCxANMuZx5RCeg0mBdEZk7DZasvvZIx
github.com/go-viper/mapstructure/v2 v2.2.1/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
github.com/golang-jwt/jwt/v5 v5.2.3 h1:kkGXqQOBSDDWRhWNXTFpqGSCMyh/PLnqUvMGJPDJDs0=
github.com/golang-jwt/jwt/v5 v5.2.3/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
@@ -102,10 +104,10 @@ go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTV
golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
golang.org/x/arch v0.8.0 h1:3wRIsP3pM4yUptoR96otTUOXI367OS0+c9eeRi9doIc=
golang.org/x/arch v0.8.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc=
golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc=
golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I=
golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
golang.org/x/crypto v0.40.0 h1:r4x+VvoG5Fm+eJcxMaY8CQM7Lb0l1lsmjGBQ6s8BfKM=
golang.org/x/crypto v0.40.0/go.mod h1:Qr1vMER5WyS2dfPHAlsOj01wgLbsyWtFn/aY+5+ZdxY=
golang.org/x/net v0.41.0 h1:vBTly1HeNPEn3wtREYfy4GZ/NECgw2Cnl+nK6Nz3uvw=
golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA=

View File

@@ -1,8 +1,8 @@
package main
import (
"GinWithGormTutorial/config"
"GinWithGormTutorial/router"
"GinTutorial/config"
"GinTutorial/router"
)
func main() {

11
models/user.go Normal file
View File

@@ -0,0 +1,11 @@
package models
import (
"gorm.io/gorm"
)
type User struct {
gorm.Model
Username string `grom:"unique"`
Password string `grom:"size:255" gorm:"not null"`
}

View File

@@ -1,6 +1,10 @@
package router
import "github.com/gin-gonic/gin"
import (
"GinTutorial/controllers"
"github.com/gin-gonic/gin"
)
func SetupRouter() *gin.Engine {
r := gin.Default()
@@ -10,12 +14,8 @@ func SetupRouter() *gin.Engine {
{
auth := v1.Group("/auth")
{
auth.POST("/login", func(context *gin.Context) {
context.JSON(200, gin.H{
"msg": "Login successfully",
})
})
auth.POST("/register")
auth.POST("/login", controllers.Login)
auth.POST("/register", controllers.Register)
}
}
}

29
utils/utils.go Normal file
View File

@@ -0,0 +1,29 @@
package utils
import (
"GinTutorial/config"
"time"
"github.com/golang-jwt/jwt/v5"
"golang.org/x/crypto/bcrypt"
)
func HashPassword(password string) (string, error) {
hash, err := bcrypt.GenerateFromPassword([]byte(password), config.AppConfig.Bcrypt.Cost)
return string(hash), err
}
func GenerateJWT(username string) (string, error) {
token := jwt.NewWithClaims(jwt.SigningMethodHS512, jwt.MapClaims{
"username": username,
"exp": time.Now().Add(time.Hour * 72).Unix(),
})
signedToken, err := token.SignedString([]byte("secret"))
return "Bearer " + signedToken, err
}
func CheckPassword(password, hash string) bool {
err := bcrypt.CompareHashAndPassword([]byte(hash), []byte(password))
return err == nil
}