JavaRush /Курси /Spring Security /Default user, generated password і вхід

Default user, generated password і вхід

Spring Security
Рівень 2 , Лекція 2
Відкрита

1. Default user у Spring Boot

Після підключення Spring Security розробникові потрібен не лише «замок на дверях», а й спосіб самому перевірити, що він відчиняється. Інакше ви насамперед замикаєте себе. У застосунку все так само: захист уже закрив доступ, а вам потрібно переконатися, що він працює і що в захищену зону справді можна потрапити як належить.

Тому Spring Boot робить прагматичну річ: якщо ви підключили spring-boot-starter-security і ще не налаштували власних користувачів, платформа створює тимчасового користувача «для першого запуску». По суті, це технічний гостьовий пропуск для розробника: з ним можна вручну перевірити, що кінцеві точки закрилися, що вхід працює і що після входу запити проходять.

В офіційних матеріалах Spring Security це показано на рівні спрощеної логіки автоконфігурації: створюється користувач із логіном user і випадковим паролем, який виводиться в консоль під час запуску застосунку.

Важливо правильно зрозуміти задум: default user не існує для того, щоб ваш продукт «працював у проді без налаштувань». Він існує для того, щоб ви не застрягли біля зачинених дверей у момент, коли security вже ввімкнулася, а прикладна модель користувачів ще не готова.

2. Default user і користувач проєкту

Коли людина вперше бачить у логах “Using generated security password…”, дуже легко подумати: «О, це і є мій користувач. Отже, у мене вже є облікові записи, ролі, права…». І ось тут потрібно спокійно пригальмувати, зробити ковток води й чесно сказати: ні, це не користувач вашої платформи. Це службовий користувач Spring Boot.

Якщо згадати терміни з першого дня, то default user — це просто наймінімальніший principal, який дає змогу пройти authentication і потрапити в застосунок. У нього є credentials — логін і пароль, — але він не відображає вашу бізнес-модель доступу. Для нього не існує ні «owner-only», ні «editor-only», ні «admin-only»; він не знає, що таке «мій чернетковий запис» і «чужий чернетковий запис», і не розуміє, що частина API взагалі має бути публічною.

У такого користувача є ще одна характерна особливість: він зазвичай один. У спрощеному прикладі автоконфігурації створюється user із роллю USER. Для сценарію «перевірити, що двері взагалі зачиняються і відчиняються» цього достатньо, але для тестування матриці доступу проєкту, де будуть різні ролі та різні зони, — уже ні.

Щоб не плутатися, зручно тримати в голові таку порівняльну таблицю:

Характеристика default user (зараз) Користувач вашого проєкту (пізніше)
Мета Швидко показати, що security працює Реальний доступ до даних і операцій продукту
Де зберігається У памʼяті застосунку, автоматично У вашій моделі (спочатку in-memory свідомо, потім БД)
Пароль Випадковий, друкується під час запуску Керований вами (і зберігається безпечно)
Ролі та права Мінімально і «для галочки» Відображають бізнес-сценарії (USER/EDITOR/ADMIN, permissions)
Час життя Для першого запуску / локальної перевірки Для реального використання застосунку

У цій лекції наше завдання не «побудувати user management». Наше завдання — не боятися default user і вміти використовувати його як діагностичний інструмент.

3. Generated password і перший вхід

Generated password у логах

Переходимо до найпрактичнішого питання дня: «Гаразд, користувач є. Де взяти пароль?» Відповідь спочатку дратує, а потім стає звичною: у логах запуску застосунку.

Коли ви запускаєте Spring Boot-застосунок із підключеним стартером, у консолі зʼявляється рядок приблизно такого вигляду:

# Фрагмент логу під час запуску Spring Boot-застосунку
Using generated security password: 8e557245-73e2-4286-969a-ff57fe326336

Такий рядок є й в офіційному вступному посібнику для servlet-застосунків: під час запуску через Gradle, Maven або Jar Spring Boot друкує “generated security password” у вивід.

Чому саме лог, а не, наприклад, файл? Тому що це максимально разовий і безпечний для запуску спосіб: пароль не лежить у репозиторії, не зберігається у відкритому вигляді в конфігурації — якщо ви самі так не зробили, — і при цьому його можна швидко скопіювати.

Тепер важливий нюанс, на якому спотикаються майже всі: пароль генерується під час запуску. Це означає, що після кожного перезапуску застосунку, особливо якщо у вас увімкнений devtools і він перезапускає контекст, пароль може змінитися. І якщо ви намагаєтеся залогінитися «паролем із вчорашнього запуску», застосунок не вередує — він просто живе в теперішньому.

Практична порада, яка рятує нерви: щойно запускаєте застосунок, одразу знайдіть рядок із паролем і тимчасово збережіть його в нотатки. Загубили — просто перезапустіть застосунок і візьміть новий пароль. Це швидше, ніж пів години переконувати себе, що Spring Security зламався.

Є й ще один нюанс: рядок є в логах. Отже, якщо ви випадково налаштували логування так, що INFO-повідомлення не показуються, пароль можна не побачити, хоча він при цьому існує. У такому разі або піднімаєте рівень логування, або — що для навчального проєкту зазвичай простіше — задаєте пароль явно через конфігурацію. Про це ми поговоримо трохи пізніше.

Перший вхід у застосунок

Коли ви вперше входите в Spring Security-застосунок, не потрібно одразу намагатися зрозуміти всю механіку. Зараз мета скромніша і корисніша: навчитися спостерігати. Нам важливо побачити, як захищений застосунок виглядає очима клієнта, і переконатися, що credentials справді дають доступ туди, куди без них не можна.

Найнаочніший клієнт для першого запуску — браузер. Це не найкращий інструмент для тестування REST API, зате він чудово показує ідею «є захищена зона → вас просять увійти». В офіційних матеріалах прямо сказано: якщо відкрити захищений URL у браузері, застосунок перенаправляє на стандартну сторінку входу.

Зафіксуємо простий спостережуваний сценарій на нашому навчальному проєкті. Додамо endpoint /api/me, який символізує особисту зону користувача.

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController // Цей контролер доступний через HTTP
public class MeController {

    @GetMapping("/api/me") // Захищена кінцева точка: без автентифікації сюди не пустить Spring Security
    public String me() {
        // Тут немає бізнес-логіки: нам важливо побачити, що запит дійшов до контролера
        return "автентифікована зона";
    }
}

Далі все просто.

Спочатку ви відкриваєте в браузері http://localhost:8080/api/me. Якщо security підключено, ви не побачите “автентифікована зона” одразу. Замість цього потрапите на сторінку входу. Це і є той самий контроль на вході, який зʼявився після підключення стартера.

Потім ви вводите логін user і той самий generated password, який знайшли в логах. Після успішного входу ви знову робите запит до /api/me — іноді браузер робить це автоматично, — і вже отримуєте відповідь контролера.

На цьому кроці важливо зрозуміти одну думку: контролер не змінювався. Ні контролер, ні бізнес-логіка, ні DTO не змінювалися. Змінилося лише одне: між клієнтом і контролером зʼявився обовʼязковий етап «доведіть, хто ви». Це і є початок реального security-шару.

Ще одна деталь, яку корисно помітити: після успішного логіну Spring Security зазвичай повертає вас до того ресурсу, який ви намагалися відкрити до входу. На рівні механіки це частина стандартної поведінки form-login: всередині фреймворка є обробник успішного входу, який робить перенаправлення до збереженого запиту. Ми поки що не розбираємо, як саме це запамʼятовується; нам важливо побачити, що UX «увійшли → повернулися туди, куди йшли» справді існує.

Щоб закріпити картину, можна уявити послідовність так:

sequenceDiagram
    %% Спостережуваний сценарій: перший вхід до захищеної кінцевої точки через браузер
    participant B as Браузер
    participant A as Застосунок
    B->>A: "GET /api/me"
    A-->>B: "Перенаправлення на сторінку входу"
    B->>A: "POST login (user + generated password)"
    A-->>B: "Вхід успішний"
    B->>A: "GET /api/me (повторно)"
    A-->>B: "200 OK + 'автентифікована зона'"

Це не «реалізація», а просто карта спостережень: що ви бачите очима клієнта під час першого запуску.

4. Перевірка доступу та зручність розробки

Що доводить вхід default user

Після першого успішного входу дуже хочеться видихнути і сказати: «Ну все, безпека зроблена». Це той самий момент, коли хочеться лагідно, але наполегливо нагадати: ні, ви просто ввімкнули світло в кімнаті. Меблі ще не розставлено.

Що саме доводить успішний вхід під default user?

Він показує, що застосунок тепер уміє вимагати authentication і уміє пропускати запити далі, якщо credentials правильні. Це вже важливий результат, тому що ми бачимо: security справді впливає на доступність кінцевих точок.

Але він не доводить, що у вас правильна матриця доступу. Чому? Тому що зараз Spring Security за замовчуванням захищає все підряд. В офіційних матеріалах це прямо сформульовано як очікування під час виконання: для будь-якої кінцевої точки потрібен автентифікований користувач, і водночас реєструється default user із паролем, який друкується в консоль.

І ось тут зʼявляється корисний навчальний контраст: у нас у проєкті є зона, яка за бізнес-сенсом публічна, наприклад /api/public/**. Але за замовчуванням вона теж закрита. Це не помилка — це стартова позиція. Ми ще не навчили застосунок розрізняти публічні й приватні зони. Поки що ми лише навчили його не пускати будь-кого.

Якщо хочете зробити швидку перевірку без браузера, можна використати curl із basic credentials. У матеріалах є такий приклад: без credentials ви отримаєте 401, а з -u user:<password> запит пройде і ви отримаєте вже «чесну» відповідь застосунку — наприклад, 404, якщо URL взагалі не існує.

# -i: показати заголовки відповіді (статус, WWW-Authenticate тощо)
# -u: передати basic credentials (ім'я користувача і пароль)
curl -i -u user:ВАШ_ЗГЕНЕРОВАНИЙ_ПАРОЛЬ http://localhost:8080/api/me

Я свідомо не заглиблююся в те, чому браузер показує одне, а curl — інше: це тема наступної лекції дня. Тут нам важливо лише зрозуміти, що credentials працюють не «магічно в браузері», а як загальний спосіб пройти перевірку особи.

Фіксуємо логін і пароль для розробки

Із generated password є побутова проблема: він випадковий, його потрібно щоразу шукати в логах, а іноді застосунок перезапускається частіше, ніж ви встигаєте кліпнути. У якийсь момент починає здаватися, що ви не вивчаєте Spring Security, а проходите квест «знайди UUID у консолі».

Spring Boot дає змогу задати логін і пароль для default user через конфігурацію. У матеріалах про security сказано прямо: можна змінити username і password, задавши spring.security.user.name і spring.security.user.password.

Для навчального проєкту це зручно зробити в application.yml локально:

spring:
  security:
    user:
      # Фіксуємо ім'я користувача для локальної розробки
      name: user
      # Фіксуємо пароль, щоб не шукати значення в логах після кожного перезапуску
      password: dev-password

Сенс тут не в тому, щоб «зробити пароль простим назавжди». Сенс у тому, щоб перестати витрачати час на пошук значення в логах і зосередитися на спостереженнях: які кінцеві точки закриті, як виглядає сторінка входу і що змінюється після успішної автентифікації.

Але є важлива дисципліна, яку хочеться закласти одразу — без страшилок, але чесно. Такий пароль не можна сприймати як норму для продакшена. По-перше, його легко забути як «навчальний» і занести в репозиторій. По-друге, у голові закріпиться небезпечна модель: «безпека = пароль у конфігурації», а ми пізніше будемо будувати нормальну модель користувачів.

Тому добрий компроміс для навчального проєкту звучить так: задаємо фіксований пароль локально, щоб не відволікатися, але тримаємо в голові, що це тимчасова заглушка.

На цьому місці корисно відокремити дві речі. default user відповідає лише на питання «чи можна взагалі пройти вхід». А ось як виглядає відмова без входу — уже окрема історія: браузер частіше побачить сторінку входу, а API-клієнт — 401. Це один і той самий baseline, просто два різні способи зустріти неавтентифікований запит.

5. Типові помилки під час першого входу

Помилка №1: намагатися увійти не з тим username.
Дуже часта ситуація: студент бачить “generated password”, але не помічає, що username за замовчуванням — user. У підсумку вводиться “admin”, “myname”, “root” (бо душа просить влади), а пароль при цьому береться правильний. Результат — «не входить», і здається, що security зламався. Насправді зламалася лише здогадка.

Помилка №2: перезапустили застосунок — а пароль у нотатках залишився старий.
Це класика. Пароль згенеровано під час запуску, ви його скопіювали, потім Gradle пересобрав проєкт, IDE зробила restart, і ви знову намагаєтеся увійти зі старим значенням. Виглядає як «раптово перестало працювати», хоча насправді просто змінилися credentials. Якщо використовуєте generated password, ставтеся до нього як до «пароля поточного запуску».

Помилка №3: шукати password у коді контролера або в application.yml, коли він генерується.
Згенерований пароль виводиться в лог запуску застосунку. Він не живе в контролерах, DTO чи сервісах, тому що не є частиною вашої бізнес-логіки. У матеріалах це показано буквально як рядок у консольному виводі “Using generated security password …”. Якщо ви шукаєте його в коді, то просто копаєте не там.

Помилка №4: вважати, що перший успішний вхід означає «доступи налаштовано».
Успішний вхід означає лише те, що authentication існує і працює. Він не означає, що /api/public/** справді публічний, що /api/admin/** справді адмінський і що логіка «свій/чужий» уже врахована. Зараз поточний baseline платформи за замовчуванням закриває все і дає один технічний пропуск усередину. Це стартова точка, а не готова модель безпеки.

Помилка №5: поставити фіксований пароль і забути, що це тимчасовий хак.
Задати spring.security.user.password — нормально для локальної розробки, і це підтриманий сценарій. Але небезпечно перетворювати це на звичку «пароль зберігається в конфігурації проєкту». На рівні курсу ми ще повернемося до нормальних підходів, але вже зараз корисно розділяти: «зручно для навчання» і «нормально для життя».

Коментарі
ЩОБ ПОДИВИТИСЯ ВСІ КОМЕНТАРІ АБО ЗАЛИШИТИ КОМЕНТАР,
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ