Spring Boot, AngularJS, Spring Security

0

Cześć.
// tl;dr -> patrz tylko 2.
1. Zamierzam stworzyć web aplikację z wykorzystaniem Springa i AngularaJS. Od jakiegoś czasu kombinuję z jedną i drugą technologią testując jak działają różne rzeczy. Niedawno udało mi się wreszcie skonsumować resta, po tym jak doczytałem, że aplikację po stronie klienta wystarczy wrzucić do folderu webapp, gdzie spring boot domyślnie szuka pliku index.html (wiem, że można rozdzielić serwer z klientem (bodajże jakieś CORSy), ale dopiero uczę się tych technologii i naprawdę chcę przejść po linii najmniejszego oporu, systematycznie uzupełniając wiedzę, bo już nie raz się zaciąłem, chcą zrobić wszystko na raz). Aplikacja będzie wymagała utworzenia konta użytkownika (nawet bez maila, wystarczy username i password) i zalogowania się.
Googlowałem już na różny sposób tagi 'spring boot angularjs security' i znalazłem masę informacji (może aż za dużo?), w tym tutorial na stronie spring.io, ale albo instrukcje są dość chaotyczne, albo mało wyjaśniają, ewentualnie są niepełne, a czasem próbują zrobić wszystko na raz (autoryzacja w przypadku rozdzielenia modułu, jakaś własna baza tokenów itp itd), kiedy ja potrzebuję czegoś najprostszego, od czego mógłbym zacząć, dlatego zdecydowałem się (narażając na opinię człowieka, który nie umie szukać/korzystać z informacji z googla) poprosić o pomoc na 4p.

-----------------

2. Chciałbym się dowiedzieć, co mi będzie niezbędne do nauczenia się, poczytania w dokumentacji (może znacie jakieś konkretne linki albo już mieliście taki problem?), aby:

  • a) zabezpieczyć resty, a dokładnie, że dostęp do konkretnych ścieżek mogą mieć tylko użytkownicy z rolą USER / ADMIN / KTOŚ_TAM;

  • b) zrobić stronę logowania w Angularze (i umożliwyć wylogowanie się);

  • c) zrobić stronę rejestracji nowego użytkownika w Angularze i zapisać tego usera do bazy danych (z samą bazą umiem się połączyć);

  • d) móc przechowywać aktualnie zalogowanego użytkownika (żeby jak ten kliknie 'moje konto' to wyświetliły się dane właśnie z jego danymi; jakiś user_context czy coś);

O ile na 'a' znalazłem całkiem niemało informacji w necie, o tyle na 'b' już mniej, a na 'c' czy 'd' - w ogóle (przynajmniej opisanych w jakiś przystępny dla laika sposób).
Jak wspominałem, pragnę poznać najprostszy sposób. Co do 'c' to rozumiem, że wystarczy encja z nazwą użytkownika, hasłem i... no właśnie rolą czy rolami? W sensie jak ktoś jest adminem to automatycznie ma też w sobie rolę 'USER'? Muszę trzymać dwie role? Czy zaznaczyć w konfiguracji security, że dane ścieżki są dostępne dla 'USER' i 'ADMIN'? Jeśli tak, to pole 'role' powinno się trzymać jako String czy Enum?

-----------------

3. Przy okazji ciekawi mnie kilka rzeczy (jeśli ktoś jest w stanie odpowiedź w 2-3 zdaniach, przedstawić esencję problemu, zamiast odsyłać do kilkudziestu stron dokumentacji :)):

  • a) Jak to jest zrobione, że dwie oddzielne rzeczy (jakimi są serwis restowy i część po stronie klienta) są w stanie dbać o autoryzację?

  • b) Skąd serwer wie, że właśnie dany użytkownik jest właśnie zalogowany? Chodzi mi o to, że angular może rzucić GET na jakąś ścieżkę a serwer ma zwrócić np. jakiegoś JSONa, ale skąd wie, że to żądanie przyszło właśnie od takiego użytkownika z danymi uprawnieniami (skoro jak zdążyliśmy ustalić, te rzeczy są odrębne)?

  • c) I jak to jest, ze np. jak wyłączymy stronę i nie wrócimy na nią po chwili, to zostajemy wylogowani? Spring liczy jakoś czas 'nieaktywności' dla każdego zalogowanego użytkownika i np. po 'x' takich minutach automatycznie wylogowuje (chyba że zaznaczymy coś w rodzaju 'rememeber me')?
    Po prostu chciałbym też nieco zrozumieć, a nie tylko bezmyślnie przepisywać kod.

Bardzo proszę o pomoc, chciałbym to zrozumieć i się tego nauczyć.

0

to bardzo dobre pytania, będę obserwował temat bo też mnie niektóre rzeczy trapiły

1

Jak wspominałem, pragnę poznać najprostszy sposób. Co do 'c' to rozumiem, że wystarczy encja z nazwą użytkownika, hasłem i... no właśnie rolą czy rolami? W sensie jak ktoś jest adminem to automatycznie ma też w sobie rolę 'USER'? Muszę trzymać dwie role? Czy zaznaczyć w konfiguracji security, że dane ścieżki są dostępne dla 'USER' i 'ADMIN'? Jeśli tak, to pole 'role' powinno się trzymać jako String czy Enum?

Tak jak zechcesz

c) zrobić stronę rejestracji nowego użytkownika w Angularze i zapisać tego usera do bazy danych (z samą bazą umiem się połączyć);

Wysyłasz POSTa z danymi i tworzysz encje. Tylko pamiętaj użyć password Encodera po drodze :)

1

a) zabezpieczyć resty, a dokładnie, że dostęp do konkretnych ścieżek mogą mieć tylko użytkownicy z rolą USER / ADMIN / KTOŚ_TAM;

Cały temat autoryzacji i autentykacji możesz oprzeć na sesji albo na tokenie. Osobiście w przypadku o którym piszesz wole użycie tokenów. Podczas logowania użytkownika wysyłasz do części backendowej POST z loginem i hasłem użytkownika. Tam sprawdzasz czy dane do autoryzacji się zgadzają i generujesz i odsylasz token w którym zapisujesz informacje potrzebne do autoryzacji (mozesz umiescic tam wszystko co tylko bedzie Ci potrzebne w komunikacji miedzy angularem a springiem). Token ten później przesyłasz w headerze każdego zapytania REST'owego, dzieki czemu spring wie czy i jakie treści może zaserwować. Podczas generacji ustawiasz także czas ważności takiego tokena (później możesz ten token odświeżać).

b) zrobić stronę logowania w Angularze (i umożliwyć wylogowanie się);

Strona logowania formularz z loginem i haslem przesylany POST'em z prosba o token. W momencie wylogowania po prostu niszczysz token w angularze i przekierowywujesz na strone logowania, to wszystko.

c) zrobić stronę rejestracji nowego użytkownika w Angularze i zapisać tego usera do bazy danych (z samą bazą umiem się połączyć);

Strone rejstracji robisz w taki sam sposób jak stronę logowania. Przesyłasz dane do założenia użytkownika do springa POST'em. Spring łączy się z bazą, zakłada użytkownika i zwraca do angulara odpowiednia wiadomość.

d) móc przechowywać aktualnie zalogowanego użytkownika (żeby jak ten kliknie 'moje konto' to wyświetliły się dane właśnie z jego danymi; jakiś user_context czy coś);

Twój user_context możesz śmiało trzymać w tokenie i z niego odczytywać w angularze.

a) Jak to jest zrobione, że dwie oddzielne rzeczy (jakimi są serwis restowy i część po stronie klienta) są w stanie dbać o autoryzację?

b) Skąd serwer wie, że właśnie dany użytkownik jest właśnie zalogowany? Chodzi mi o to, że angular może rzucić GET na jakąś ścieżkę a serwer ma zwrócić np. jakiegoś JSONa, ale skąd wie, że to żądanie przyszło właśnie od takiego użytkownika z danymi uprawnieniami (skoro jak zdążyliśmy ustalić, te rzeczy są odrębne)?

W springu tworzysz sobie odpowiedni filter który przy każdym zapytaniu sprawdza czy przesłano w nagłówku token, waliduje poprawność tokena. Jeśli wszystko się zgadza, to przetwarzasz dalej to zapytanie, jeśli nie, to odsylasz 403. Filtr ten może obejmować tylko konkretne ścieżki (np. strony /login udostepniasz dla wszystkich, ale już dla strony /mojekonto/** filtrujesz wszystkie zapytania).

I jak to jest, ze np. jak wyłączymy stronę i nie wrócimy na nią po chwili, to zostajemy wylogowani? Spring liczy jakoś czas 'nieaktywności' dla każdego zalogowanego użytkownika i np. po 'x' takich minutach automatycznie wylogowuje (chyba że zaznaczymy coś w rodzaju 'rememeber me')?

Podczas każdorazowej walidacji tokenu sprawdzasz jego czas wygaśnięcia.

Postać takiego tokena możesz sobie sam wymyślić, jednak są przyjęte standardy których warto się trzymać. Polecam JSON Web Tokens (https://jwt.io/) do których bez problemu znajdziesz biblioteki prawie w każdym języku.

0

Token trzymać w bazie tak?
Np. encje User i UserToken z relacją 1:1?
Czy Spring ma jakieś ułatwienia co do tego?
No i każdy request ma być sprawdzany pod kątem praw dostępu, czy Spring to daje z automatu?

1

Tokena nie musisz trzymać po stronie back-endu, dzięki czemu powiedzmy, że masz zachowaną bezstanowość. W momencie generowania tokena przy logowaniu (ewentualnie przy późniejszym odświeżaniu), podpisujesz go sobie. Nikt Ci go zatem nie zmieni, ani nie podrobi. Jeśli chodzi o ułatwienia to wszystko zrobi za ciebie spring security + jose4j / java-jwt. Każdy request jest sprawdzany pod kątem praw dostępu i robi to za Ciebie spring z automatu w momencie gdy zdefiniujesz sobie filter sprawdzajacy token.

0

Dziękuję za odpowiedzi.

@scibi92 ->
Chciałbym to ja poznać dobre praktyki :) W sensie są jakieś różnice że lepiej do takich rzeczy Enum albo String?
Rozumiem, że wysyłam POSTa który zawiera username i normalny password, a dopiero po stronie javy w controllerze hashuje go i porównuje z tym co siedzi w bazie danych, o ile odpowiedni username istnieje?


Zakładając że wszystkie resty bedę miał w '/api/**', a strony będą miały jakieś normalne intuicyjne nazwy. Zrobię sobie panel boczny i tam 2 zakładki: 'twoje konto', 'panel admina'. Żeby to drugie było widoczne tylko dla admina, mógłbym użyć ng-If=user.isAdmin() i sprawdzić czy posiada odpowiednią rolę. Ale z tego co wiem, nie ma chyba problemu, żeby ktoś sobie otworzył konsolę i zmienił na ng-If=true. Czy może nie muszę się tym przejmować, póki operacje znajdujące się w panelu admina oraz dane się tam wyświetlające, będą dostępne z restów /api/admin/**, do których user sobie dostępu nie umożliwi z poziomu JSa?

0

Wymienione przez ciebie technologie wykorzystywane są w generatorze kodu JHipster. Możesz sobie wygenerować kod odpowiedzialny za logowanie z wykorzystaniem tokenów (albo zwykłe logowanie sesyjne) i podejrzeć jak to jest zaimplementowane.

0
Naitoreivun napisał(a):

Dziękuję za odpowiedzi.
Zakładając że wszystkie resty bedę miał w '/api/**', a strony będą miały jakieś normalne intuicyjne nazwy. Zrobię sobie panel boczny i tam 2 zakładki: 'twoje konto', 'panel admina'. Żeby to drugie było widoczne tylko dla admina, mógłbym użyć ng-If=user.isAdmin() i sprawdzić czy posiada odpowiednią rolę. Ale z tego co wiem, nie ma chyba problemu, żeby ktoś sobie otworzył konsolę i zmienił na ng-If=true. Czy może nie muszę się tym przejmować, póki operacje znajdujące się w panelu admina oraz dane się tam wyświetlające, będą dostępne z restów /api/admin/**, do których user sobie dostępu nie umożliwi z poziomu JSa?

Co z tego, że sobie podmieni coś w DOM skoro nic to nie da, bo treści serwuje backend (Java) i ona zadba o to, co moze widziec

0

W sensie są jakieś różnice że lepiej do takich rzeczy Enum albo String?

Ale gdzie?

Rozumiem, że wysyłam POSTa który zawiera username i normalny password, a dopiero po stronie javy w controllerze hashuje go i porównuje z tym co siedzi w bazie danych, o ile odpowiedni username istnieje?

No tak

Zarejestruj się i dołącz do największej społeczności programistów w Polsce.

Otrzymaj wsparcie, dziel się wiedzą i rozwijaj swoje umiejętności z najlepszymi.