From bafeb1c10091a49426073408939db8b3ca080e01 Mon Sep 17 00:00:00 2001 From: maksim Date: Fri, 21 Feb 2025 19:43:34 +0400 Subject: [PATCH] =?UTF-8?q?=D0=9C=D0=BE=D0=B4=D1=83=D0=BB=D1=8C=20=D0=B0?= =?UTF-8?q?=D0=B2=D1=82=D0=BE=D1=80=D0=B8=D0=B7=D0=B0=D1=86=D0=B8=D0=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- go-auth/.gitignore | 2 + go-auth/README.md | 3 + go-auth/controllers/authController.go | 138 ++++++++++++++++++++++++++ go-auth/database/connection.go | 32 ++++++ go-auth/main.go | 32 ++++++ go-auth/models/user.go | 8 ++ go-auth/routes/routes.go | 13 +++ 7 files changed, 228 insertions(+) create mode 100644 go-auth/.gitignore create mode 100644 go-auth/README.md create mode 100644 go-auth/controllers/authController.go create mode 100644 go-auth/database/connection.go create mode 100644 go-auth/main.go create mode 100644 go-auth/models/user.go create mode 100644 go-auth/routes/routes.go diff --git a/go-auth/.gitignore b/go-auth/.gitignore new file mode 100644 index 0000000..eaeb163 --- /dev/null +++ b/go-auth/.gitignore @@ -0,0 +1,2 @@ +.env +go.mod \ No newline at end of file diff --git a/go-auth/README.md b/go-auth/README.md new file mode 100644 index 0000000..7ef54cb --- /dev/null +++ b/go-auth/README.md @@ -0,0 +1,3 @@ +# PIbd-42_Kashin_M.I_FinalQualifyingWork + +Модуль "Авторизация" \ No newline at end of file diff --git a/go-auth/controllers/authController.go b/go-auth/controllers/authController.go new file mode 100644 index 0000000..466d6b7 --- /dev/null +++ b/go-auth/controllers/authController.go @@ -0,0 +1,138 @@ +package controllers + +import ( + "github.com/gofiber/fiber/v2" + "github.com/golang-jwt/jwt" + "golang.org/x/crypto/bcrypt" + "log" + "main/database" + "main/models" + "os" + "strconv" + "time" + + "github.com/joho/godotenv" +) + +// Глобальная переменная для хранения ключа +var SecretKey string + +func init() { + // Загружаем .env файл + err := godotenv.Load() + if err != nil { + log.Fatal("Ошибка загрузки .env файла") + } + + // Читаем ключ из переменной окружения + SecretKey = os.Getenv("JWT_SECRET_KEY") + if SecretKey == "" { + log.Fatal("JWT_SECRET_KEY не задан в .env") + } +} + +// Регистрируем пользователя +func Register(c *fiber.Ctx) error { + var data map[string]string + if err := c.BodyParser(&data); err != nil { + return err + } + + if data["password"] == "" { + return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"message": "Password is required"}) + } + + // Проверка на существующего пользователя + var existingUser models.User + if err := database.DB.Where("email = ?", data["email"]).First(&existingUser).Error; err == nil { + return c.Status(fiber.StatusConflict).JSON(fiber.Map{"message": "Email already taken"}) + } + + password, _ := bcrypt.GenerateFromPassword([]byte(data["password"]), bcrypt.DefaultCost) + user := models.User{ + Name: data["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 { + return c.Status(fiber.StatusNotFound).JSON(fiber.Map{"message": "User not found"}) + } + + if err := bcrypt.CompareHashAndPassword(user.Password, []byte(data["password"])); err != nil { + return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{"message": "Incorrect password"}) + } + + claims := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.StandardClaims{ + Issuer: strconv.Itoa(int(user.Id)), + ExpiresAt: time.Now().Add(time.Hour * 12).Unix(), + }) + + token, err := claims.SignedString([]byte(SecretKey)) + if err != nil { + return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"message": "Token invalid"}) + } + + cookie := fiber.Cookie{ + Name: "jwt", + Value: token, + Expires: time.Now().Add(time.Hour * 12), + HTTPOnly: true, + SameSite: "Strict", + } + c.Cookie(&cookie) + + return c.JSON(fiber.Map{"message": "Login success"}) +} + +// Получаем пользователя по токену +func User(c *fiber.Ctx) error { + cookie := c.Cookies("jwt") + token, err := jwt.ParseWithClaims(cookie, &jwt.StandardClaims{}, func(token *jwt.Token) (interface{}, error) { + return []byte(SecretKey), nil + }) + + if err != nil { + return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{"message": "Unauthorized"}) + } + + claims := token.Claims.(*jwt.StandardClaims) + if claims.ExpiresAt < time.Now().Unix() { + return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{"message": "Token expired"}) + } + + var user models.User + database.DB.Where("id = ?", claims.Issuer).First(&user) + + return c.JSON(user) +} + +// Логаут +func Logout(c *fiber.Ctx) error { + cookie := fiber.Cookie{ + Name: "jwt", + Value: "", + Expires: time.Now().Add(-time.Hour), + HTTPOnly: true, + SameSite: "Strict", + } + + c.Cookie(&cookie) + + return c.JSON(fiber.Map{"message": "Logout success"}) +} diff --git a/go-auth/database/connection.go b/go-auth/database/connection.go new file mode 100644 index 0000000..537a40b --- /dev/null +++ b/go-auth/database/connection.go @@ -0,0 +1,32 @@ +package database + +import ( + "github.com/joho/godotenv" + "gorm.io/driver/postgres" + "gorm.io/gorm" + "log" + "os" +) + +var DB *gorm.DB + +func Connect() { + // Загружаем .env + err := godotenv.Load() + if err != nil { + log.Fatal("Ошибка загрузки .env") + } + + dsn := os.Getenv("DB_URL") + if dsn == "" { + log.Fatal("Переменная DB_URL не задана") + } + + db, err := gorm.Open(postgres.Open(dsn), &gorm.Config{}) + if err != nil { + log.Fatal("Ошибка подключения к БД:", err) + } + + DB = db + log.Println("Подключение к БД успешно!") +} diff --git a/go-auth/main.go b/go-auth/main.go new file mode 100644 index 0000000..30acf05 --- /dev/null +++ b/go-auth/main.go @@ -0,0 +1,32 @@ +package main + +import ( + "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v2/middleware/cors" + "github.com/joho/godotenv" + "log" + "main/database" + "main/routes" +) + +func main() { + + // Загружаем .env + err := godotenv.Load() + if err != nil { + log.Fatal("Ошибка загрузки .env") + } + + database.Connect() + + app := fiber.New() + + app.Use(cors.New(cors.Config{ + AllowCredentials: true, + AllowOrigins: "http://localhost:8000", + })) + + routes.Setup(app) + + app.Listen(":8000") +} diff --git a/go-auth/models/user.go b/go-auth/models/user.go new file mode 100644 index 0000000..32344fc --- /dev/null +++ b/go-auth/models/user.go @@ -0,0 +1,8 @@ +package models + +type User struct { + Id uint `json:"id"` + Name string `json:"name"` + Email string `json:"email" gorm:"unique"` + Password []byte `json:"-"` +} diff --git a/go-auth/routes/routes.go b/go-auth/routes/routes.go new file mode 100644 index 0000000..5fd2ca0 --- /dev/null +++ b/go-auth/routes/routes.go @@ -0,0 +1,13 @@ +package routes + +import ( + "github.com/gofiber/fiber/v2" + "main/controllers" +) + +func Setup(app *fiber.App) { + app.Post("/api/register", controllers.Register) + app.Post("/api/login", controllers.Login) + app.Get("/api/user", controllers.User) + app.Post("/api/logout", controllers.Logout) +}