Browse Source

Autentikasi dengan jwt

widia
Widyarhn 2 years ago
parent
commit
33e515ca98
  1. 1
      .env
  2. 116
      controllers/usersController.go
  3. BIN
      go-crud.exe
  4. 2
      go.mod
  5. 4
      go.sum
  6. 5
      main.go
  7. 65
      middleware/requireAuth.go
  8. 1
      migrate/migrate.go
  9. 9
      models/userModel.go

1
.env

@ -1,2 +1,3 @@
PORT=8000 PORT=8000
SECRET=sdc87sdf6gv87df6v8sd7f6v8sdf76v8sdf7v6
DB_URL="host=dumbo.db.elephantsql.com user=yvvpeiee password=QwAhcTtiKL8j9qRyUBIqs9kNbu3O0_FB dbname=yvvpeiee port=5432 sslmode=disable" DB_URL="host=dumbo.db.elephantsql.com user=yvvpeiee password=QwAhcTtiKL8j9qRyUBIqs9kNbu3O0_FB dbname=yvvpeiee port=5432 sslmode=disable"

116
controllers/usersController.go

@ -0,0 +1,116 @@
package controllers
import (
"net/http"
"os"
"time"
"go-crud/initializers"
"go-crud/models"
"github.com/gin-gonic/gin"
"github.com/golang-jwt/jwt/v4"
"golang.org/x/crypto/bcrypt"
)
func SignUp(c *gin.Context) {
// Get the email/pass off req body
var body struct {
Email string
Password string
}
if err := c.ShouldBind(&body); err != nil {
c.JSON(http.StatusBadRequest, gin.H{
"error": "Failed to read body",
})
return
}
// Hash the password
hash, err := bcrypt.GenerateFromPassword([]byte(body.Password), 10)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{
"error": "Failed to generate pass hash",
})
return
}
// Create the user
user := models.User{Email: body.Email, Password: string(hash)}
result := initializers.DB.Create(&user)
if result.Error != nil {
c.JSON(http.StatusBadRequest, gin.H{
"error": "Failed to create user",
})
return
}
// Respond
c.JSON(http.StatusOK, gin.H{})
}
func LogIn(c *gin.Context) {
// Get the email/pass off req body
var body struct {
Email string
Password string
}
if err := c.ShouldBind(&body); err != nil {
c.JSON(http.StatusBadRequest, gin.H{
"error": "Failed to read body",
})
return
}
// Look up requested user
var user models.User
initializers.DB.First(&user, "email = ?", body.Email)
if user.ID == 0 {
c.JSON(http.StatusBadRequest, gin.H{
"error": "Invalid email or pass",
})
return
}
// Compare sent in pass with saved user pass hash
err := bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(body.Password))
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{
"error": "Invalid email or pass",
})
return
}
// Generate a jwt token
token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
"sub": user.ID,
"exp": time.Now().Add(time.Hour * 24 * 30).Unix(),
})
// Sign and get the complete encoded token as a string using the secret token
tokenString, err := token.SignedString([]byte(os.Getenv("SECRET")))
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{
"error": "Failed to create token",
})
return
}
// Send it back
c.SetSameSite(http.SameSiteLaxMode)
c.SetCookie("Authorization", tokenString, 3600*24*30, "", "", false, true)
c.JSON(http.StatusOK, gin.H{
"token": tokenString,
})
}
func Validate(c *gin.Context) {
user, _ := c.Get("user")
c.JSON(http.StatusOK, gin.H{
"message": user,
})
}

BIN
go-crud.exe

Binary file not shown.

2
go.mod

@ -7,6 +7,7 @@ require gorm.io/driver/postgres v1.5.2
require ( require (
github.com/bytedance/sonic v1.9.2 // indirect github.com/bytedance/sonic v1.9.2 // indirect
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect
github.com/dgrijalva/jwt-go v3.2.0+incompatible
github.com/fatih/color v1.9.0 // indirect github.com/fatih/color v1.9.0 // indirect
github.com/fsnotify/fsnotify v1.4.9 // indirect github.com/fsnotify/fsnotify v1.4.9 // indirect
github.com/gabriel-vasile/mimetype v1.4.2 // indirect github.com/gabriel-vasile/mimetype v1.4.2 // indirect
@ -17,6 +18,7 @@ require (
github.com/go-playground/universal-translator v0.18.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/go-playground/validator/v10 v10.14.1 // indirect github.com/go-playground/validator/v10 v10.14.1 // indirect
github.com/goccy/go-json v0.10.2 // indirect github.com/goccy/go-json v0.10.2 // indirect
github.com/golang-jwt/jwt/v4 v4.5.0 // indirect
github.com/jackc/pgpassfile v1.0.0 // indirect github.com/jackc/pgpassfile v1.0.0 // indirect
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect
github.com/jackc/pgx/v5 v5.3.1 // indirect github.com/jackc/pgx/v5 v5.3.1 // indirect

4
go.sum

@ -6,6 +6,8 @@ github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 h1:qSGYFH7+jGhD
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk= github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/fatih/color v1.9.0 h1:8xPHl4/q1VyqGIPif1F+1V3Y3lSmrq01EabUW3CoW5s= github.com/fatih/color v1.9.0 h1:8xPHl4/q1VyqGIPif1F+1V3Y3lSmrq01EabUW3CoW5s=
github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU=
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
@ -26,6 +28,8 @@ github.com/go-playground/validator/v10 v10.14.1 h1:9c50NUPC30zyuKprjL3vNZ0m5oG+j
github.com/go-playground/validator/v10 v10.14.1/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU= github.com/go-playground/validator/v10 v10.14.1/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU=
github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= 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/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg=
github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=

5
main.go

@ -3,6 +3,7 @@ package main
import ( import (
"go-crud/controllers" "go-crud/controllers"
"go-crud/initializers" "go-crud/initializers"
"go-crud/middleware"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
) )
@ -14,6 +15,10 @@ func init() {
func main() { func main() {
r := gin.Default() r := gin.Default()
r.POST("/signup", controllers.SignUp)
r.POST("/login", controllers.LogIn)
r.GET("/validate", middleware.RequireAuth, controllers.Validate)
r.POST("/posts", controllers.PostsCreate) r.POST("/posts", controllers.PostsCreate)
r.PUT("/posts/:id", controllers.PostsUpdate) r.PUT("/posts/:id", controllers.PostsUpdate)
r.GET("/posts", controllers.PostsIndex) r.GET("/posts", controllers.PostsIndex)

65
middleware/requireAuth.go

@ -0,0 +1,65 @@
package middleware
import (
"fmt"
"net/http"
"os"
"time"
"go-crud/initializers"
"go-crud/models"
"github.com/gin-gonic/gin"
"github.com/golang-jwt/jwt/v4"
)
func RequireAuth(c *gin.Context) {
// Get the cookie off req
tokenString, err := c.Cookie("Authorization")
if err != nil {
c.AbortWithStatus(http.StatusUnauthorized)
return
}
// Decode/validate it
token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"])
}
// hmacSampleSecret is a []byte containing your secret, e.g. []byte("my_secret_key")
return []byte(os.Getenv("SECRET")), nil
})
if err != nil || !token.Valid {
c.AbortWithStatus(http.StatusUnauthorized)
return
}
claims, ok := token.Claims.(jwt.MapClaims)
if !ok {
c.AbortWithStatus(http.StatusUnauthorized)
return
}
// Check the exp
if exp, ok := claims["exp"].(float64); ok && float64(time.Now().Unix()) > exp {
c.AbortWithStatus(http.StatusUnauthorized)
return
}
// Find the user with token sub
var user models.User
initializers.DB.First(&user, claims["sub"])
if user.ID == 0 {
c.AbortWithStatus(http.StatusUnauthorized)
return
}
// Attach to req
c.Set("user", user)
// Continue
c.Next()
}

1
migrate/migrate.go

@ -11,4 +11,5 @@ func init() {
func main() { func main() {
initializers.DB.AutoMigrate(&models.Post{}) initializers.DB.AutoMigrate(&models.Post{})
initializers.DB.AutoMigrate(&models.Event{}) initializers.DB.AutoMigrate(&models.Event{})
initializers.DB.AutoMigrate(&models.User{})
} }

9
models/userModel.go

@ -0,0 +1,9 @@
package models
import "gorm.io/gorm"
type User struct {
gorm.Model
Email string `gorm:"unique"`
Password string
}
Loading…
Cancel
Save