Способ аутентификации через телеграм отлично описан в документации. В этой статье мы реализуем его в Spring Boot приложении.
Создаем туннель с помощью ngrok
Для аутентификации нам необходим домен и если у вас его нет, вы можете использовать ngrok, чтобы создать временный туннель, через который ваше приложение будет доступно в интернете.
Мы запустим ngrok в докере, вот так будет выглядеть docker-compose.yaml:
services: ngrok: image: ngrok/ngrok:latest #команды для запуска ngrok command: - "start" - "--all" - "--config" - "/etc/ngrok.yml" volumes: - ./ngrok.yml:/etc/ngrok.yml #файл с настройками network_mode: host
network_mode: host
нужно, чтобы контейнер использовал порты хоста и перенаправлял трафик на хост, а не в контейнер.
Для работы ngrok потребуется зарегистрироваться на их сайте и получить токен.
После этого, в той же папке, где находится docker-compose.yaml, создаем файл конфигурации ngrok.yml и добавляем туда токен:
version: 2 authtoken: YOUR_NGROK_AUTHTOKEN tunnels: httpbin: proto: http addr: 8080
Теперь весь трафик, проходящий через созданный нгроком туннель, будет перенаправлен на localhost:8080.
После запуска контейнера перейдите на localhost:4040, там будет url, по которому ваше приложение доступно в интернете. Url будет оставаться неизменным, пока вы не перезапустите контейнер.
Создаем бота для аутентификации
Далее перейдите в BotFather, создайте бота, который будет использован для аутентификации, и привяжите к нему полученный url. Для этого перейдите в Bot Settings -> Domain -> Edit domain и отправьте url:
Далее переходим на страницу документации, вставляем логин бота в поле для него, выбираем способ авторизации «Callback».
При авторизации Callback будет вызвана функция TelegramAuth, в которую передастся объект с данными пользователя.
Скопируем сгенерированный код и укажем в функции TelegramAuth эндпоинт нашего сервера, на который надо отправить данные для проверки.
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Telegram auth</title> </head> <body> <script async src="https://telegram.org/js/telegram-widget.js?22" data-telegram-login="your-bot-login" data-size="large" data-onauth="onTelegramAuth(user)"></script> <script type="text/javascript"> function onTelegramAuth(user) { fetch("https://adc8-176-50-96-51.ngrok-free.app/auth/telegram/token", {method: 'POST', body: JSON.stringify(user)}) .then(response => response.text()) .then(body => { alert(body) }); } </script> </body> </html>
Создаем приложение и контроллер для аутентификации
После создания обычного Spring Boot прилоения помещаем html файл в папку resources/static, создаем контроллер, который будет отдавать наш html (getAuthScript()
) и проверять данные, полученные от телеграма (authenticate()
)
import org.springframework.core.io.ClassPathResource; import org.springframework.core.io.Resource; import org.springframework.http.HttpHeaders; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; import javax.crypto.Mac; import javax.crypto.spec.SecretKeySpec; import java.security.InvalidKeyException; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.Map; import static java.nio.charset.StandardCharsets.UTF_8; @RestController @RequestMapping("/auth/telegram") public class AuthController { private final String tgBotToken = "YOUR_TELEGRAM_BOT_TOKEN"; /** * возвращает html со скриптом для аутентификации */ @GetMapping public ResponseEntity<Resource> getAuthScript() { Resource resource = new ClassPathResource("static/telegramAuth.html"); var headers = new HttpHeaders(); headers.add(HttpHeaders.CONTENT_DISPOSITION, "inline; filename=telegramAuth.html"); return ResponseEntity.ok().headers(headers).body(resource); } /** * сюда отправляются данные, полученные после аутентификации */ @PostMapping public String authenticate(@RequestBody Map<String, Object> telegramData) { return telegramDataIsValid(telegramData) ? "pretend-that-it-is-your-token" : "error"; } /** * проверяет данные, полученные из телеграм */ private boolean telegramDataIsValid(Map<String, Object> telegramData) { //получаем хэш, который позже будем сравнивать с остальными данными String hash = (String) telegramData.get("hash"); telegramData.remove("hash"); //создаем строку проверки - сортируем все параметры и объединяем их в строку вида: //auth_date=<auth_date>\nfirst_name=<first_name>\nid=<id>\nusername=<username> StringBuilder sb = new StringBuilder(); telegramData.entrySet().stream() .sorted(Map.Entry.comparingByKey()) .forEach(entry -> sb.append(entry.getKey()).append("=").append(entry.getValue()).append("\n")); sb.deleteCharAt(sb.length() - 1); String dataCheckString = sb.toString(); try { //генерируем SHA-256 хэш из токена бота MessageDigest digest = MessageDigest.getInstance("SHA-256"); byte[] key = digest.digest(tgBotToken.getBytes(UTF_8)); //создаем HMAC со сгенерированным хэшем Mac hmac = Mac.getInstance("HmacSHA256"); SecretKeySpec secretKeySpec = new SecretKeySpec(key, "HmacSHA256"); hmac.init(secretKeySpec); // добавляем в HMAC строку проверки и переводим в шестнадцатеричный формат byte[] hmacBytes = hmac.doFinal(dataCheckString.getBytes(UTF_8)); StringBuilder validateHash = new StringBuilder(); for (byte b : hmacBytes) { validateHash.append(String.format("%02x", b)); } // сравниваем полученный от телеграма и сгенерированный хэш return hash.contentEquals(validateHash); } catch (NoSuchAlgorithmException | InvalidKeyException e) { throw new RuntimeException(e); } } }
Алгоритм реализации функции проверки (telegramDataIsValid()
) так же описан в документации:
Проверить аутентификацию и целостность полученных данных можно, сравнив полученный хэш‑параметр с шестнадцатеричным представлением подписи HMAC‑SHA-256 строки проверки данных с хешем SHA256 токена бота, используемого в качестве секретного ключа.
Строка проверки данных представляет собой объединение всех полученных полей, отсортированных в алфавитном порядке, в формате key= с символом перевода строки (‘\n’, 0×0A), используемым в качестве разделителя.
После запуска приложения переходим на https://adc8-176-50-96-51.ngrok-free.app/auth/telegram и проходим аутентификацию в телеграме.
Заключение
Статья написана в процессе разработки стартапа на Spring Boot 🦄
Подписывайтесь, если было интересно
ссылка на оригинал статьи https://habr.com/ru/articles/848502/
Добавить комментарий