package main import ( "log" "os" "sync" "time" "github.com/gofiber/fiber/v2" "github.com/golang-jwt/jwt/v4" "golang.org/x/crypto/bcrypt" "gorm.io/driver/sqlite" "gorm.io/gorm" "github.com/joho/godotenv" ) // Модель пользователя type User struct { Id uint `json:"id" gorm:"primaryKey"` Name string `json:"name"` Email string `json:"email" gorm:"unique"` Password []byte `json:"-"` } // Глобальные переменные var ( DB *gorm.DB SecretKey string blockedTokens = make(map[string]struct{}) blockedTokensMu sync.Mutex ) // Инициализация базы данных и загрузка переменных окружения func init() { if err := godotenv.Load(); err != nil { log.Fatal("Ошибка загрузки .env файла") } SecretKey = os.Getenv("JWT_SECRET_KEY") if SecretKey == "" { log.Fatal("JWT_SECRET_KEY не задан в .env") } var err error DB, err = gorm.Open(sqlite.Open("users.db"), &gorm.Config{}) if err != nil { log.Fatal("Ошибка подключения к базе данных:", err) } DB.AutoMigrate(&User{}) } // Генерация JWT-токена func generateToken(user User) (string, error) { claims := jwt.MapClaims{ "user_id": user.Id, "exp": time.Now().Add(time.Hour * 24).Unix(), } token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) return token.SignedString([]byte(SecretKey)) } // Проверка заблокированного токена func isTokenBlocked(token string) bool { blockedTokensMu.Lock() defer blockedTokensMu.Unlock() _, exists := blockedTokens[token] return exists } // Блокировка токена при выходе func blockToken(token string) { blockedTokensMu.Lock() defer blockedTokensMu.Unlock() blockedTokens[token] = struct{}{} } // Регистрация пользователя func Register(c *fiber.Ctx) error { var data map[string]string if err := c.BodyParser(&data); err != nil { log.Println("Ошибка парсинга запроса:", err) return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"message": "Invalid request body"}) } if data["password"] == "" { return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"message": "Password is required"}) } var existingUser User if err := DB.Where("email = ?", data["email"]).First(&existingUser).Error; err == nil { return c.Status(fiber.StatusConflict).JSON(fiber.Map{"message": "Email already taken"}) } passwordHash, err := bcrypt.GenerateFromPassword([]byte(data["password"]), bcrypt.DefaultCost) if err != nil { log.Println("Ошибка хеширования пароля:", err) return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"message": "Could not process password"}) } user := User{ Name: data["name"], Email: data["email"], Password: passwordHash, } DB.Create(&user) log.Println("Пользователь зарегистрирован:", user.Email) return c.JSON(user) } // Логин пользователя func Login(c *fiber.Ctx) error { var data map[string]string if err := c.BodyParser(&data); err != nil { log.Println("Ошибка парсинга запроса:", err) return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"message": "Invalid request body"}) } var user User DB.Where("email = ?", data["email"]).First(&user) if user.Id == 0 { return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{"message": "Invalid email or password"}) } if err := bcrypt.CompareHashAndPassword(user.Password, []byte(data["password"])); err != nil { return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{"message": "Invalid email or password"}) } token, err := generateToken(user) if err != nil { log.Println("Ошибка генерации JWT токена:", err) return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"message": "Could not generate token"}) } c.Cookie(&fiber.Cookie{ Name: "jwt", Value: token, Expires: time.Now().Add(time.Hour * 24), HTTPOnly: true, Secure: true, SameSite: "Strict", }) log.Println("Пользователь вошел в систему:", user.Email) return c.JSON(fiber.Map{"message": "Login successful"}) } // Получение информации о пользователе func UserInfo(c *fiber.Ctx) error { cookie := c.Cookies("jwt") if isTokenBlocked(cookie) { return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{"message": "Token is blocked"}) } token, err := jwt.ParseWithClaims(cookie, &jwt.MapClaims{}, func(token *jwt.Token) (interface{}, error) { return []byte(SecretKey), nil }) if err != nil { return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{"message": "Unauthorized"}) } claims, ok := token.Claims.(*jwt.MapClaims) if !ok || !token.Valid { return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{"message": "Invalid token"}) } var user User DB.Where("id = ?", (*claims)["user_id"]).First(&user) if user.Id == 0 { return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{"message": "User not found"}) } return c.JSON(user) } // Выход пользователя func Logout(c *fiber.Ctx) error { cookie := c.Cookies("jwt") blockToken(cookie) c.Cookie(&fiber.Cookie{ Name: "jwt", Value: "", Expires: time.Now().Add(-time.Hour), HTTPOnly: true, Secure: true, SameSite: "Strict", }) log.Println("Пользователь вышел из системы") return c.JSON(fiber.Map{"message": "Logout successful"}) } // Запуск приложения и маршрутизация func main() { app := fiber.New() app.Post("/register", Register) app.Post("/login", Login) app.Get("/user", UserInfo) app.Post("/logout", Logout) log.Println("Сервер запущен на порту 3000") log.Fatal(app.Listen(":3000")) }