Безопасность и авторизация
Механизм аутентификации
Система использует JWT (JSON Web Token) с библиотекой JJWT. Сессии — stateless: сервер не хранит состояние сессии, каждый запрос содержит токен.
Жизненный цикл токена
1. POST /api/v1/auth/login
Body: { email, password }
→ AuthServiceImpl.authenticate()
→ AuthenticationManager.authenticate() # проверка пароля (BCrypt)
→ JwtService.generateToken(UserDetails) # создание JWT
Response: { token: "eyJ...", user: { id, name, email, role } }
2. Клиент сохраняет токен:
localStorage.setItem('dm_shop_token', token)
3. Axios Interceptor (src/lib/api.ts):
config.headers.Authorization = `Bearer ${token}`
4. JwtAuthFilter (Spring Security Filter Chain):
→ Читает заголовок Authorization
→ JwtService.extractUsername(token)
→ UserDetailsService.loadUserByUsername(email)
→ SecurityContextHolder.setAuthentication(authToken)
Параметры JWT
| Параметр | Значение | Переменная окружения |
|---|---|---|
| Алгоритм подписи | HMAC-SHA256 | — |
| Секретный ключ | 64-байтный hex | JWT_SECRET |
| Время жизни | 24 часа (86 400 000 мс) | JWT_EXPIRATION_MS |
| Хранение на клиенте | localStorage | — |
Роли пользователей
Три роли определены в UserRole:
| Роль | Описание |
|---|---|
USER | Зарегистрированный покупатель: просмотр, оформление заказов |
MANAGER | Менеджер: обработка заказов, обращений |
ADMIN | Полный доступ ко всему |
Правила доступа
Публичные эндпоинты (без токена)
POST /api/v1/auth/register
POST /api/v1/auth/login
GET /api/v1/products/**
GET /api/v1/categories/**
GET /api/v1/brands
GET /api/v1/reviews/active
GET /api/v1/banner-blocks/active
GET /api/v1/feature-items/active
GET /api/v1/config/public
GET /api/v1/images/{id}
POST /api/v1/cart/validate
POST /api/v1/contacts
GET /swagger-ui/**
GET /v3/api-docs/**
Только авторизованные (роль USER+)
POST /api/v1/orders
GET /api/v1/orders/**
PUT /api/v1/auth/profile
PUT /api/v1/auth/change-password
Только ADMIN / MANAGER
/api/v1/admin/**
POST /api/v1/reviews
PUT /api/v1/reviews/**
DELETE /api/v1/reviews/**
POST /api/v1/images
PUT /api/v1/images/**
DELETE /api/v1/images/**
/api/v1/banner-blocks (POST/PUT/DELETE)
/api/v1/feature-items (POST/PUT/DELETE)
/api/v1/config/**
Примечание: На момент текущей версии некоторые эндпоинты настроены через
permitAll()вSecurityConfig— механизм проверки токена работает, но принудительная авторизация ещё не везде активна. Строгий контроль планируется.
Конфигурация Spring Security
// SecurityConfig.java — ключевые настройки
http
.csrf(AbstractHttpConfigurer::disable) // REST API — CSRF не нужен
.sessionManagement(s -> s.sessionCreationPolicy(STATELESS))
.addFilterBefore(jwtAuthFilter, UsernamePasswordAuthenticationFilter.class)
.authorizeHttpRequests(auth -> auth
.requestMatchers("/api/v1/auth/**").permitAll()
.requestMatchers("/api/v1/admin/**").hasAnyRole("ADMIN", "MANAGER")
// ...
.anyRequest().permitAll() // временно
);
Хеширование паролей
Используется BCryptPasswordEncoder (стандартный Spring Security). Пароли в базе данных хранятся только в виде bcrypt-хешей, не в открытом виде.
CORS
Настраивается в WebConfig через свойство cors.allowed-origins:
# application-local.yaml
cors:
allowed-origins: http://localhost:3000
# Production
cors:
allowed-origins: https://shop.example.com
Разрешены методы: GET, POST, PUT, PATCH, DELETE, OPTIONS.
Разрешены все заголовки (*).
Разрешены credentials.
Регистрация и профиль
POST /api/v1/auth/register
Body: { name, email, password }
→ Проверка уникальности email (DuplicateEmailException если занят)
→ BCrypt.encode(password)
→ UserEntity.role = USER
→ Возвращает JWT токен сразу
PUT /api/v1/auth/profile
Body: { name, phone }
→ Требует токен (текущий пользователь)
PUT /api/v1/auth/change-password
Body: { currentPassword, newPassword }
→ Проверяет currentPassword через BCrypt.matches()