1. Trzeba zamknąć endpoint
Gdy zespół mówi „ten endpoint trzeba zamknąć", prawie zawsze ma na myśli dwa różne zadania.
Pierwsze zadanie: zrozumieć, kto wysyła żądanie. To pytanie o tożsamość callera. System musi odróżnić anonimowego klienta od znanego użytkownika, a jednego użytkownika od drugiego. To właśnie robi authentication.
Drugie zadanie: zrozumieć, co temu callerowi wolno. Nawet jeśli system wie już, kto przyszedł, nie oznacza to jeszcze, że działanie jest dopuszczalne. Zwykły użytkownik może się zalogować z powodzeniem, a i tak nie mieć prawa do publikowania contentu ani zmieniania ról innych użytkowników. Tę pracę robi authorization.
Bardzo ważne jest wyczucie, że to nie dwie nazwy dla tej samej weryfikacji. To dwie różne decyzje. Publiczne odczytanie opublikowanego artykułu to dobry przykład, gdzie authorization mówi „dozwolone", choć authentication w ogóle nie musi się odbyć. A próba wywołania operacji edytora przez zwykłego użytkownika to dobry przykład, gdzie authentication już się powiodła, a authorization i tak mówi „nie".
2. Authentication: kto właściwie wysyła żądanie
Authentication odpowiada na pytanie „kto jest przede mną?". W klasycznym scenariuszu odbywa się to przez login i hasło, ale na poziomie idei ważniejsze jest co innego: system musi uzyskać wiarygodny sposób powiązania żądania z konkretnym callerem.
record LoginRequest(String username, String password) {}
W takim modelu password to nie „dane profilu". To coś, czym caller potwierdza swoją tożsamość. Pojawia się tu ważne słowo: credentials. Credentials to dane potwierdzenia. W scenariuszu username/password jest to hasło. Później w kursie zobaczysz inne formy potwierdzenia, ale na razie wystarczy uchwycić samą zasadę.
Po pomyślnym potwierdzeniu system nie musi już ciągać za sobą całego domenowego profilu użytkownika. Do decyzji security zazwyczaj potrzebuje bardziej kompaktowego obiektu – principal. Principal to security-reprezentacja callera, a nie cała biografia użytkownika z bazy. Niekiedy to username, niekiedy id, niekiedy kombinacja kilku pól. Chodzi o to, żeby warstwa security nie nosiła za sobą zbędnej domenowej wagi.
Bardzo przydatne jest wyczucie różnicy między dwoma zdaniami. „Mamy użytkownika w bazie" – to jeszcze nie authentication. „System uznaje bieżące żądanie za żądanie od konkretnego callera" – to już bliżej sedna. Bez tego powiązania cały talk o dostępie wisi w powietrzu.
3. Authorization: użytkownikowi nie wolno wszystkiego 🚦
Gdy caller stał się znany systemowi, pojawia się następne pytanie: „a co mu teraz wolno?". Tu żyje authorization.
Wyobraź sobie zwykłe scenariusze naszego projektu. GET /api/public/articles może być dozwolony bez potwierdzonego użytkownika. GET /api/me wymaga już znanego callera. POST /api/editor/drafts/{id}/publish wymaga nie tylko znanego callera, ale i określonego zestawu uprawnień. A GET /api/drafts/{id} podnosi jeszcze jeden poziom złożoności: nie wystarczy wiedzieć, kto to jest; trzeba zrozumieć, czy ma on związek z konkretnym obiektem.
Właśnie tu warto przyzwyczaić się do prostej myśli: użytkownik może być uwierzytelniony, a mimo to nie być autoryzowany. To nie jest „zepsuty system", lecz wręcz przeciwnie – dowód, że system potrafi rozróżniać stany. Wie, kto przyszedł, i przy tym wciąż może powiedzieć: „ta osoba nie powinna wykonywać tego działania".
Działa to też odwrotnie: działanie może być autoryzowane bez autentykacji. Publiczny artykuł to dozwolony scenariusz dla anonimowego klienta. Oznacza to, że authentication i authorization nie tylko różnią się znaczeniem, ale i nie muszą zawsze chodzić parą „albo oba są, albo żadnego".
4. Principal, credentials, role i authority: jak ich nie mieszać 🧩
Teraz można złożyć podstawowy słownik w jedną kompaktową mapę. Terminy poniżej nie są po to, żeby zdać egzamin ze słownika, lecz żeby jednakowo dyskutować o systemie w kodzie, konfiguracji i w zespole.
| Termin | Prosty sens | Co łatwo pomylić |
|---|---|---|
| principal | kto jest teraz uznawany za callera z punktu widzenia security | pełna domenowa encja użytkownika |
| credentials | czym caller potwierdza swoją tożsamość | dane profilu |
| role | duża kategoria dostępu | dokładne prawo do działania |
| authority | bardziej precyzyjne prawo do działania | cała rola |
Role w naszym projekcie to na przykład USER, EDITOR, ADMIN. To wygodna, gruboziarnista warstwa podziału dostępu. Authority to już bardziej precyzyjne prawo, jak draft:publish lub user:manage. Nie każdy kurs musi głęboko schodzić do szczegółowego modelu uprawnień, ale rozróżnianie dużych ról i precyzyjnych praw jest przydatne od samego początku.
Osobno ważne: nie należy podmieniać ownership rolą. Reguła „to mój szkic" nie mieszka wewnątrz USER jako takiego. Wszyscy zwykli użytkownicy mogą mieć tę samą rolę, ale różne szkice. Dlatego owner-only to nie odmiana roli, lecz odrębny typ weryfikacji oparty na relacji między callerem a konkretnym obiektem.
5. Session, token i SecurityContext – na razie na poziomie idei
Na pierwszym poziomie nie potrzebujesz jeszcze szczegółów o filtrach i wewnętrznej mechanice, ale trzy słowa warto wprowadzić już teraz, żeby później nie było poczucia „skąd to się w ogóle wzięło".
Session to model stanowy, w którym serwer pamięta, że żądania od tego klienta należą do jednego i tego samego już potwierdzonego użytkownika. Token to inny sposób powiązania żądania z callerem: klient przynosi ze sobą specjalny artefakt dostępu, a serwer na jego podstawie odtwarza tożsamość i uprawnienia. To różne modele i kurs specjalnie pokaże oba, nie mieszając ich w jedno.
Ważne jest jeszcze jedno subtelne rozróżnienie: hasła i bearer token nie warto nazywać pełnymi synonimami. W klasycznym login-flow hasło to credentials. W modelu bezstanowym token staje się odrębnym artefaktem dostępu, z własnymi regułami życia i własnymi ryzykami. Jeśli te słowa wymieszać zawczasu, rozmowa o modelach stanowych i bezstanowych zacznie się potem plątać.
SecurityContext na poziomie idei to miejsce, gdzie aplikacja przechowuje aktualny obraz security żądania. Kto jest teraz callerem? Czy jest uznawany za uwierzytelnionego? Jakie ma uprawnienia? Na razie wystarczy ta ogólna myśl. Później zobaczysz, jak ten obraz jest technicznie składany. Dziś ważne jest tylko to, żeby rozumieć, że system musi mieć „miejsce prawdy" o bieżącym callerze.
6. Bez słownika Spring Security wygląda jak magia 🪄
Gdy słowa są wymieszane, framework zawsze wygląda groźniej, niż jest. Wtedy SecurityContext wydaje się tajemniczym kontenerem „czegoś o użytkowniku", roles i authorities zaczynają się rozmazywać, token i credentials brzmią jak wymienne słowa, a authentication i authorization zamieniają się w jeden rozmyty termin „no tak, bezpieczeństwo".
Ale gdy słownik staje na miejscu, wiele się upraszcza 💡 Później spotkasz SecurityContext, AuthenticationManager, SecurityFilterChain, request-level rules i method-level rules. I nie będą już wisieć w powietrzu. Będziesz mógł czytać je przez zwykłe ludzkie pytania: kto wysyła żądanie, czym to jest potwierdzone, gdzie przechowywany jest aktualny obraz security i co temu callerowi wolno.
Właśnie dlatego słownika nie można traktować jako suchej „wyspy teoretycznej". Nie jest potrzebny sam w sobie, lecz po to, żeby następna mechanika czytała się jako logika inżynierska, a nie magia. Gdy język nie jest złożony, każda konfiguracja security zamienia się w kopiowanie zaklęć. Gdy język jest złożony, konfiguracja zaczyna być wytłumaczalna.
7. Typowe błędy 🙀
Błąd nr 1: używać authentication i authorization jako jednego słowa.
To najbardziej podstawowe pomieszanie, z którego potem wyrasta prawie wszystko inne. Najpierw system musi rozpoznać, kto jest callerem. Dopiero potem – co mu wolno. Niekiedy to pierwsze już jest, a tego drugiego wciąż nie ma. Niekiedy to drugie trywialnie zezwala na działanie nawet bez tego pierwszego.
Błąd nr 2: uważać principal za pełną domenową encję użytkownika.
Gdy do warstwy security zaczyna się wciągać cały profil, zbędne pola i dane biznesowe, system staje się cięższy i mniej przejrzysty. Warstwa security zazwyczaj potrzebuje zwartej odpowiedzi na pytanie „kto to?", a nie całej historii użytkownika.
Błąd nr 3: mylić rolę i ownership.
Rola USER nie wie, czyj dokładnie to szkic. Jeśli próbujesz rozwiązać zadanie „swój/cudzy" tylko przez role, prawie na pewno otworzysz zbędny dostęp lub, odwrotnie, zablokujesz potrzebny scenariusz.
Błąd nr 4: nazywać session i token tymi samymi mechanizmami.
Oba pomagają powiązać żądanie z callerem, ale robią to zasadniczo różnymi sposobami. Jeśli zbyt wcześnie zatrzeć tę różnicę, później trudno zrozumieć, dlaczego modele stanowy i bezstanowy mają tak różne kompromisy.
GO TO FULL VERSION