- 🔐 Безопасная аутентификация через JWT
- 💳 Просмотр своих банковских карт с маскированными номерами
- 📱 Пагинация и фильтрация списка карт
- 🔄 Переводы между своими картами
⚠️ Запрос на блокировку карты- 📊 Просмотр баланса
- 👥 Полное управление пользователями (CRUD)
- 🃏 Создание, блокировка, активация и удаление карт
- 👁️ Просмотр всех карт и переводов в системе
- 🔍 Детальная информация по любому пользователю
- ⚙️ Управление статусами карт
- 🔒 Шифрование номеров карт (AES-256)
- 🎭 Маскирование данных при отображении
- 🛂 Ролевой доступ (ADMIN/USER)
- ⏱️ JWT токены с TTL
- 🌐 Настраиваемый CORS
| Компонент | Технология | Назначение |
|---|---|---|
| Backend Framework | Spring Boot 3.5.7 | Основной фреймворк |
| Language | Java 17 | Язык программирования |
| Database | PostgreSQL 17 | Основная СУБД |
| Authentication | JWT + Spring Security | Аутентификация и авторизация |
| API Documentation | OpenAPI 3.1 + Swagger | Документация API |
| Migrations | Liquibase | Управление миграциями БД |
| Containerization | Docker + Docker Compose | Контейнеризация |
| Testing | JUnit 5 + Mockito | Модульное тестирование |
| Code Generation | Lombok | Сокращение шаблонного кода |
| Validation | Spring Validation | Валидация данных |
┌─────────────────────────────────────────┐
│ BankCards System │
├─────────────────────────────────────────┤
│ Spring Boot 3.5.7 │
│ ├── Spring Web MVC │
│ ├── Spring Security + JWT │
│ ├── Spring Data JPA │
│ ├── Spring Validation │
│ └── SpringDoc OpenAPI │
│ │
│ PostgreSQL 17 (Docker) │
│ Liquibase (миграции) │
│ Docker Compose (оркестрация) │
└─────────────────────────────────────────┘
- Java 17 или выше
- Maven 3.6 или выше
- Docker и Docker Compose
- Git (для клонирования репозитория)
Скопируйте файл с примером переменных окружения:
cp .env.example .envОтредактируйте файл .env, установив свои значения:
# Database Configuration
POSTGRES_DB=bankcards
POSTGRES_USER=your_username_here # Измените на свой
POSTGRES_PASSWORD=your_password_here # Измените на свой
# JWT Configuration
JWT_SECRET=your_base64_jwt_secret_here # Base64 ключ для HS256 (32 байта)
JWT_EXPIRATION=86400000 # В миллисекундах (24 часа)
# Encryption
SECRET_KEY=your_base64_aes_key_here # Base64 ключ для AES-256 (32 байта)Генерация секретных ключей:
# Генерация JWT секрета (минимум 32 байта)
openssl rand -base64 32
# Генерация AES-256 ключа (32 байта)
openssl rand -base64 32# Очистка и сборка проекта (без тестов)
mvn clean package -DskipTests
# Или сборка с тестами
mvn clean packageАртефакт будет создан в: target/bankcards-1.0.0-SNAPSHOT.jar
# Сборка и запуск всех сервисов
docker-compose up --build
# Запуск в фоновом режиме
docker-compose up -d --build
# Просмотр логов
docker-compose logs -f app
# Остановка всех сервисов
docker-compose down
# Остановка с удалением томов данных
docker-compose down -vДля запуска из среды разработки (IntelliJ IDEA, Eclipse, VS Code):
- Добавьте файл
.envв конфигурацию запуска - Или установите переменные окружения вручную (настройка
SPRING_DATASOURCEтребуется обязательно, даже с.envфайлом):
SPRING_DATASOURCE_URL=jdbc:postgresql://localhost:5432/bankcards
SPRING_DATASOURCE_USERNAME=postgres
SPRING_DATASOURCE_PASSWORD=your_password
JWT_SECRET=your_base64_jwt_secret
SECRET_KEY=your_base64_aes_key
CREATE TABLE users (
id BIGSERIAL PRIMARY KEY,
username VARCHAR(255) UNIQUE NOT NULL,
password_hash VARCHAR(255) NOT NULL,
role VARCHAR(50) NOT NULL,
created_at TIMESTAMP NOT NULL,
updated_at TIMESTAMP
);CREATE TABLE cards (
id BIGSERIAL PRIMARY KEY,
card_number_hash VARCHAR(255) UNIQUE NOT NULL,
last_four_digits VARCHAR(4) NOT NULL,
expiry_date DATE NOT NULL,
balance DECIMAL(15,2) NOT NULL,
card_status VARCHAR(50) NOT NULL,
user_id BIGINT NOT NULL REFERENCES users(id),
created_at TIMESTAMP NOT NULL,
updated_at TIMESTAMP
);CREATE TABLE transfer (
id BIGSERIAL PRIMARY KEY,
user_id BIGINT NOT NULL REFERENCES users(id),
from_card_id BIGINT NOT NULL REFERENCES cards(id),
to_card_id BIGINT NOT NULL REFERENCES cards(id),
amount DECIMAL(15,2) NOT NULL,
created_at TIMESTAMP NOT NULL
);| Роль | Доступные операции |
|---|---|
| USER | Просмотр своих карт, переводы между своими картами, блокировка своих карт |
| ADMIN | Все операции USER + управление пользователями, полный доступ ко всем картам и переводам |
- Номера карт: Шифруются AES-256 перед сохранением в БД
- Отображение: Маскированный формат
**** **** **** 1234 - Пароли: Хэшируются с использованием BCrypt
- Токены: JWT с HS256 алгоритмом и сроком действия
Конфигурация позволяет безопасные кросс-доменные запросы:
cors:
origins: # Разрешенные источники
methods: # Разрешенные HTTP методы
- POST
- GET
- PUT
- DELETE
headers: "*" # Разрешенные заголовки
allowedCredentials: true
cache:
maxAge: 3600 # Время кэширования предварительных запросовАутентификация пользователя и получение JWT токена.
Запрос:
{
"username": "testuser",
"password": "123456"
}Ответ:
{
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"userId": 1,
"generationTime": "2024-01-15T10:30:00Z",
"expirationTime": "2024-01-16T10:30:00Z"
}Получение списка карт пользователя с пагинацией.
Параметры запроса:
| Параметр | Тип | Описание | Пример |
|---|---|---|---|
page |
Integer | Номер страницы (начинается с 0) | 0 |
size |
Integer | Количество элементов на странице | 10 |
sort |
String | Поле для сортировки | id,asc или createdAt,desc |
Пример запроса:
GET /api/cards?page=0&size=10&sort=id,asc
Пример ответа:
{
"content": [
{
"id": 1,
"hiddenNumber": "**** **** **** 1234",
"status": "ACTIVE",
"expiryDate": "2025-12-31",
"balance": 1500.75,
"createdAt": "2024-01-15T10:30:00Z",
"updatedAt": "2024-01-15T10:30:00Z"
}
],
"page": {
"size": 10,
"number": 0,
"totalElements": 25,
"totalPages": 3
}
}Получение информации о конкретной карте.
GET /api/cards/1
Запрос на блокировку карты.
POST /api/cards/block/1
Создание перевода между своими картами.
Запрос:
{
"fromCard": 1,
"toCard": 2,
"amount": 100.50
}Получение информации о текущем пользователе.
GET /api/users
GET /api/admin/users- Все пользователи (с пагинацией)POST /api/admin/users- Создание пользователяGET /api/admin/users/{userId}- Информация о пользователеDELETE /api/admin/users/{userId}- Удаление пользователя
GET /api/admin/cards- Все карты в системе (с пагинацией)POST /api/admin/cards- Создание новой картыPOST /api/admin/cards/block/{cardId}- Принудительная блокировкаPOST /api/admin/cards/activate/{cardId}- Активация картыDELETE /api/admin/cards/{cardId}- Удаление карты
GET /api/admin/transfers- Все переводы (с пагинацией)GET /api/admin/transfers/{transferId}- Информация о переводе
Проект включает модульные тесты для ключевой бизнес-логики:
# Запуск всех тестов
mvn test
# Запуск тестов с отчетом
mvn test surefire-report:reportУчтите, что запуск тестов требует ручного включения .env файла и настройки переменных окружения SPRING_DATASOURCE_URL, SPRING_DATASOURCE_USERNAME, SPRING_DATASOURCE_PASSWORD
- JUnit 5 - основной тестовый фреймворк
- Mockito - мокирование зависимостей
- Spring Security Test - тестирование безопасности
- Spring Boot Test - интеграционное тестирование
| Сервис | Образ | Порт | Назначение |
|---|---|---|---|
| postgres | postgres:17 |
5432:5432 | PostgreSQL база данных |
| app | Сборка из Dockerfile | 8080:8080 | Spring Boot приложение |
services:
postgres:
image: postgres:17
container_name: bankcards-postgres
environment:
POSTGRES_DB: ${POSTGRES_DB}
POSTGRES_USER: ${POSTGRES_USER}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
ports:
- "5432:5432"
volumes:
- postgres_data:/var/lib/postgresql/data
app:
build: .
container_name: bankcards-app
env_file:
- .env
environment:
SPRING_DATASOURCE_URL: jdbc:postgresql://postgres:5432/${POSTGRES_DB}
SPRING_DATASOURCE_USERNAME: ${POSTGRES_USER}
SPRING_DATASOURCE_PASSWORD: ${POSTGRES_PASSWORD}
ports:
- "8080:8080"
depends_on:
- postgresserver:
port: 8080
spring:
application:
name: bankcards
datasource:
url: ${SPRING_DATASOURCE_URL}
username: ${SPRING_DATASOURCE_USERNAME}
password: ${SPRING_DATASOURCE_PASSWORD}
jpa:
hibernate:
ddl-auto: validate # Валидация схемы без изменений
show-sql: false
liquibase:
enabled: true
change-log: classpath:db/migration/db.changelog-master.yaml
springdoc:
api-docs:
enabled: true
path: /v3/api-docs
swagger-ui:
enabled: true
path: /swagger-ui.html
logging:
level: # Здесь настраиваются уровни логирования
'[com.example.bankcards]': DEBUG
'[org.springframework.security]': INFO
application:
jwt:
secret: ${JWT_SECRET}
expiration: ${JWT_EXPIRATION}
encryption:
secret-key: ${SECRET_KEY}| Переменная | Описание | Пример |
|---|---|---|
SPRING_DATASOURCE_URL |
URL подключения к БД | jdbc:postgresql://localhost:5432/bankcards |
SPRING_DATASOURCE_USERNAME |
Имя пользователя БД | postgres |
SPRING_DATASOURCE_PASSWORD |
Пароль БД | admin |
JWT_SECRET |
Секрет для подписи JWT | base64_encoded_string_32_bytes |
SECRET_KEY |
Ключ для шифрования AES-256 | base64_encoded_string_32_bytes |
Решение:
# Проверьте статус контейнера PostgreSQL
docker-compose ps
# Перезапустите сервисы
docker-compose restart postgres
# Проверьте логи PostgreSQL
docker-compose logs postgresРешение:
- Убедитесь, что
JWT_SECRETв.envфайле совпадает с использованным при генерации - Перезапустите приложение после изменения секрета
Решение:
- Убедитесь, что
SECRET_KEYимеет длину 32 байта в Base64 формате - Сгенерируйте новый ключ:
openssl rand -base64 32