Расскажите подробнее о вашем последнем проекте или одной из ключевых задач, над которой вы работали.

Ответ

На последнем проекте я разрабатывал микросервис управления пользователями (аутентификация, авторизация, профиль) на Go.

Стек технологий:

  • Фреймворк: Echo
  • База данных: PostgreSQL для хранения данных пользователей.
  • Кэш: Redis для кэширования сессий и часто запрашиваемых данных.

Ключевые задачи, которые я решал:

  • Реализация JWT-аутентификации: Разработал middleware для проверки JWT-токенов и ролевого доступа (RBAC). Токены содержали информацию о userID и role.
  • Оптимизация работы с БД: Использовал pgx с пулом соединений (connection pooling) и prepared statements для защиты от SQL-инъекций и ускорения выполнения повторяющихся запросов.
  • Написание тестов: Покрыл бизнес-логику юнит-тестами с помощью testify/assert и написал интеграционные тесты с использованием dockertest. dockertest позволял поднимать временный инстанс PostgreSQL в Docker для каждого тестового прогона, обеспечивая полную изоляцию тестов.

Пример аутентификационного middleware для фреймворка Echo:

import (
    "net/http"
    "strings"

    "github.com/golang-jwt/jwt/v4"
    "github.com/labstack/echo/v4"
)

// Claims определяет структуру данных, хранимых в JWT
type Claims struct {
    UserID int `json:"user_id"`
    jwt.RegisteredClaims
}

var jwtSecret = []byte("your_secret_key")

// AuthMiddleware проверяет заголовок Authorization и валидирует JWT
func AuthMiddleware(next echo.HandlerFunc) echo.HandlerFunc {
    return func(c echo.Context) error {
        authHeader := c.Request().Header.Get("Authorization")
        if authHeader == "" {
            return c.JSON(http.StatusUnauthorized, "missing authorization header")
        }

        // Токен обычно передается в формате "Bearer <token>"
        tokenString := strings.TrimPrefix(authHeader, "Bearer ")
        if tokenString == authHeader { // Префикс не найден
             return c.JSON(http.StatusUnauthorized, "invalid token format")
        }

        claims := &Claims{}
        token, err := jwt.ParseWithClaims(tokenString, claims, func(token *jwt.Token) (interface{}, error) {
            return jwtSecret, nil
        })

        if err != nil || !token.Valid {
            return c.JSON(http.StatusUnauthorized, "invalid token")
        }

        // Сохраняем ID пользователя в контексте запроса для дальнейшего использования
        c.Set("userID", claims.UserID)
        return next(c)
    }
}

Проект велся по методологии Scrum с двухнедельными спринтами. Для CI/CD мы использовали GitLab CI, который автоматически собирал, тестировал и деплоил приложение в кластер Kubernetes.