Backend-приложение для безопасного подтверждения операций с помощью временных кодов (OTP), отправляемых через Telegram. Проект выполнен в рамках учебного кейса от компании Promo IT.
- Возможности
- Технологии
- Требования
- Установка и настройка
- Сборка и запуск
- API-эндпоинты
- Структура проекта
- Логирование
- Возможные проблемы и их решение
- Регистрация и аутентификация пользователей (JWT-токены).
- Автоматическое назначение роли `ADMIN` первому зарегистрированному пользователю.
- Генерация OTP-кода заданной длины и времени жизни.
- Отправка кода через Telegram (эмуляция SMS и Email не реализована).
- Валидация OTP с проверкой срока действия и статуса.
- Фоновый процесс автоматической прострочки кодов (статус `EXPIRED`).
- API администратора: просмотр и удаление обычных пользователей, изменение параметров OTP.
- Хранение данных в PostgreSQL 17, работа через JDBC.
- Логирование с помощью SLF4J + Logback.
- Сборка с Maven, упаковка в исполняемый JAR.| Компонент | Технология |
|------------------|--------------------------|
| Язык | Java 17 |
| Сборщик | Maven |
| HTTP-сервер | `com.sun.net.httpserver` |
| База данных | PostgreSQL 17 |
| Доступ к БД | JDBC (чистый SQL) |
| Аутентификация | JWT (io.jsonwebtoken) |
| Хеширование | BCrypt (jBCrypt) |
| JSON | Jackson |
| Логирование | SLF4J + Logback |
| Уведомления | Telegram Bot API |- **Java 17** или выше.
- **Maven 3.6+**.
- **PostgreSQL 17** (должен быть запущен локально на порту `5432`).
- **Токен Telegram-бота** и **Chat ID** (для отправки сообщений).git clone https://github.com/lavren007/otp-service.git
cd otp-serviceУбедитесь, что PostgreSQL запущен, и у вас есть пользователь postgres с паролем postgres (либо измените параметры подключения в файле DatabaseConnection.java).
При первом запуске приложение автоматически создаст базу данных otp_db и все необходимые таблицы.
Если хотите создать БД вручную:
CREATE DATABASE otp_db;Параметры подключения по умолчанию (можно изменить в DatabaseConnection.java):
private static final String URL = "jdbc:postgresql://localhost:5432/otp_db";
private static final String USER = "postgres";
private static final String PASSWORD = "postgres";1. Откройте Telegram и найдите [@BotFather](https://t.me/botfather).
2. Отправьте команду `/newbot` и следуйте инструкциям.
3. После создания вы получите **токен** бота (например, `1234567890:ABCdefGHIjklMNOpqrsTUVwxyz`).
4. Найдите своего бота в Telegram по username и отправьте ему любое сообщение (например, "Привет").
5. Получите ваш **Chat ID**, перейдя по ссылке:https://api.telegram.org/bot<ВАШ_ТОКЕН>/getUpdates
В ответе найдите поле `chat` → `id`.
Пример:
```json
"chat": {
"id": 123456789,
...
}
Запомните это число (может быть отрицательным для групповых чатов).
Перед запуском приложения необходимо задать две переменные окружения:
- TELEGRAM_BOT_TOKEN – токен вашего бота.
- TELEGRAM_CHAT_ID – ваш Chat ID.
TELEGRAM_BOT_TOKEN=ваш_токен_бота
TELEGRAM_CHAT_ID=ваш_chat_idset TELEGRAM_BOT_TOKEN=ваш_токен_бота
set TELEGRAM_CHAT_ID=ваш_chat_idexport TELEGRAM_BOT_TOKEN="ваш_токен_бота"
export TELEGRAM_CHAT_ID="ваш_chat_id"
⚠️ Если переменные не заданы, приложение запустится, но отправка кодов в Telegram будет невозможна (в логах появится предупреждение).
mvn clean packageПосле успешной сборки в папке target/ появится файл otp-service-1.0.0.jar.
TELEGRAM_BOT_TOKEN="ваш_токен_бот" \
TELEGRAM_CHAT_ID="ваш_chat_id" \
java -jar target/otp-service-1.0.0.jarПосле запуска вы увидите в консоли сообщение:
Server started on port 8080
API endpoints available at http://localhost:8080
Сервер будет слушать порт 8080. Чтобы изменить порт, отредактируйте SERVER_PORT в классе AppConfig.
Все эндпоинты (кроме /api/auth/*) требуют передачи JWT-токена в заголовке:
Authorization: Bearer <ваш_токен>
Формат данных – JSON.
Создаёт нового пользователя.
Первый зарегистрированный пользователь автоматически получает роль ADMIN.
Все последующие – роль USER.
Запрос (macOS / Linux):
curl -X POST http://localhost:8080/api/auth/register \
-H "Content-Type: application/json" \
-d '{"username":"admin","password":"admin123"}'Запрос (Windows cmd):
curl -X POST http://localhost:8080/api/auth/register -H "Content-Type: application/json" -d "{\"username\":\"admin\",\"password\":\"admin123\"}"Ответ (200 OK):
{
"token": "eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJhZG1pbiIsInJvbGUiOiJBRE1JTiIsImlhdCI6MTYxMDAwMDAwMCwiZXhwIjoxNjEwMDAzNjAwfQ.abc123..."
}Ошибки:
400 Bad Request– имя пользователя уже занято или не переданы поля.
Аутентифицирует пользователя и возвращает JWT-токен.
Запрос (macOS / Linux):
curl -X POST http://localhost:8080/api/auth/login \
-H "Content-Type: application/json" \
-d '{"username":"admin","password":"admin123"}'Ответ (200 OK):
{
"token": "eyJhbGciOiJIUzI1NiJ9..."
}Ошибки:
401 Unauthorized– неверное имя пользователя или пароль.
Требуют валидный JWT-токен (роль может быть любой).
Генерирует уникальный operationId и OTP-код, отправляет код через Telegram, а также сохраняет его в файл otp_codes.txt в корне проекта.
Запрос (macOS / Linux):
curl -X POST http://localhost:8080/api/user/otp/generate \
-H "Authorization: Bearer <токен>"Ответ (200 OK):
{
"operationId": "550e8400-e29b-41d4-a716-446655440000",
"message": "OTP sent via Telegram and saved to file"
}В Telegram-бот придёт сообщение вида:
Your OTP code is: 483201
Проверяет правильность кода для указанной операции. При успехе переводит код в статус USED.
Запрос (macOS / Linux):
curl -X POST http://localhost:8080/api/user/otp/validate \
-H "Authorization: Bearer <токен>" \
-H "Content-Type: application/json" \
-d '{"operationId":"550e8400-e29b-41d4-a716-446655440000","code":"483201"}'Ответ (200 OK):
{
"valid": true
}Если код неверен / просрочен / уже использован:
{
"valid": false
}Доступны только пользователям с ролью ADMIN.
Требуют JWT-токен администратора.
Возвращает всех пользователей, кроме администраторов.
Запрос (macOS / Linux):
curl -X GET http://localhost:8080/api/admin/users \
-H "Authorization: Bearer <админский_токен>"Ответ (200 OK):
[
{
"id": 2,
"username": "user1",
"passwordHash": "$2a$10$...",
"role": "USER"
},
{
"id": 3,
"username": "user2",
"passwordHash": "$2a$10$...",
"role": "USER"
}
]Удаляет пользователя с указанным ID и все его OTP-коды. Нельзя удалить администратора.
Запрос (macOS / Linux):
curl -X DELETE http://localhost:8080/api/admin/users/2 \
-H "Authorization: Bearer <админский_токен>"Ответ (200 OK):
{
"message": "User deleted"
}Ошибки:
404 Not Found– пользователь с таким ID не найден.400 Bad Request– попытка удалить администратора.
Позволяет изменить длину генерируемого кода и время его жизни (в секундах).
Запрос (macOS / Linux):
curl -X PUT http://localhost:8080/api/admin/config \
-H "Authorization: Bearer <админский_токен>" \
-H "Content-Type: application/json" \
-d '{"codeLength":8,"ttlSeconds":600}'Ответ (200 OK):
{
"message": "Config updated"
}Параметры:
| Поле | Тип | Описание |
|---|---|---|
codeLength |
int | Количество цифр в коде (≥ 4) |
ttlSeconds |
int | Время жизни кода в секундах (≥ 1) |
src/main/java/com/example/otpservice/
├── Main.java # Точка входа, инициализация БД и сервера
├── config/
│ └── AppConfig.java # Константы приложения (порт)
├── controller/ # Обработчики HTTP-запросов
│ ├── BaseHandler.java
│ ├── AuthController.java
│ ├── UserController.java
│ └── AdminController.java
├── service/ # Бизнес-логика
│ ├── AuthService.java
│ ├── OtpService.java
│ ├── UserService.java
│ ├── NotificationService.java
│ └── ExpiredOtpCleanupService.java
├── dao/ # Доступ к базе данных (JDBC)
│ ├── DatabaseConnection.java
│ ├── UserDao.java
│ ├── OtpConfigDao.java
│ └── OtpCodeDao.java
├── model/ # Сущности
│ ├── User.java
│ ├── OtpConfig.java
│ ├── OtpCode.java
│ ├── Role.java
│ └── OtpStatus.java
├── util/ # Вспомогательные классы
│ ├── JwtUtil.java
│ ├── PasswordUtil.java
│ ├── OtpGenerator.java
│ ├── JsonUtil.java
│ └── TelegramNotifier.java
└── middleware/ # Фильтры HTTP
├── AuthFilter.java
└── LoggingFilter.java # Логирование всех запросов
Для тестирования API вы можете использовать:
- cURL (командная строка) – примеры приведены выше.
- Postman – удобный графический HTTP-клиент.
- Insomnia – легковесная альтернатива Postman.
- HTTP-клиент в IDE (IntelliJ IDEA Ultimate, VS Code с REST Client).
- Зарегистрируйте первого пользователя (станет администратором).
- Войдите, чтобы получить токен.
- Сгенерируйте OTP (проверьте Telegram и файл
otp_codes.txt). - Введите полученный код для валидации – должно вернуться
{"valid":true}. - Попробуйте повторно использовать тот же код – должно быть
false. - Подождите истечения TTL (по умолчанию 300 секунд) и повторите валидацию – код станет
EXPIRED.
В проекте реализовано два уровня логирования:
- Логирование всех входящих HTTP-запросов (через
LoggingFilter): метод, URI, статус ответа, время обработки. - Логирование ключевых событий в сервисах (регистрация, вход, генерация/валидация OTP, ошибки).
Логи выводятся в консоль. Формат и уровень можно настроить в src/main/resources/logback.xml.
Симптом: При запуске приложение падает с ошибкой Connection refused.
Решение:
- Убедитесь, что PostgreSQL запущен (
sudo systemctl status postgresqlна Linux илиbrew services list | grep postgresqlна macOS). - Проверьте параметры подключения в
DatabaseConnection.java(порт, пользователь, пароль). - Попробуйте подключиться вручную:
psql -U postgres -h localhost.
Симптом: OTP генерируется, но сообщение в Telegram не приходит.
Решение:
- Проверьте, что переменные окружения
TELEGRAM_BOT_TOKENиTELEGRAM_CHAT_IDзаданы и видны процессу Java. - Убедитесь, что вы отправили боту хотя бы одно сообщение перед запуском приложения (для активации чата).
- Проверьте логи приложения – там будут сообщения об ошибках при отправке в Telegram.
Симптом: java.net.BindException: Address already in use.
Решение:
- Остановите предыдущий экземпляр приложения (Ctrl+C в терминале, где он запущен).
- Или измените порт в
AppConfig.java.
Разработчик: [Лаврентьев Антон]
Контакты: [email: anton_lavren@mail.ru]