Przełączanie użytkownika z generowaniem JWT w Spring Security

Przełączanie użytkownika z generowaniem JWT w Spring Security
SI
  • Rejestracja:ponad 3 lata
  • Ostatnio:14 dni
  • Postów:62
0

Chodzi o mechanizm przełączania kont użytkowników podobny jak w Google, YouTube...

Architektura:

  • serwer autentykacyjny z użyciem Spring Security
  • inne mikroserwisy (dostęp tylko z tokenem JWT)
  • load balancer weryfikujący token JWT
  • aplikacja webowa

Działa to tak, że użytkownik loguje się w aplikacji webowej i serwer autentykacyjny generuje token JWT.

Kopiuj
POST /token?username=user&password=pass&grant_type=password&scope=read

Oprócz tego wysyłany jest nagłówek Authorization typu basic, gdzie uwierzytelniamy aplikację kliencką.

Kopiuj
Authorization: Basic ...........

W Spring Security dużo rzeczy dzieje się automatycznie. Jeśli nie ma sesji zalogowanego użytkownika, to endpoint /token działa, weryfikuje użytkownika i zwraca ważny token JWT. Ale teraz chcemy przełączyć się na inne konto.

Dane wejściowe, jakimi dysponujemy:

  • token JWT zalogowanego użytkownika
  • ID użytkownika, na jakiego chcemy przełączyć (będzie weryfikacja, czy to user powiązany)
  • nie chcę w aplikacji webowej przechowywać wpisanego loginu i hasła (kwestie bezpieczeństwa)

Dawniej Spring Security posiadał możliwość wielu sesji, ale już nie ma. Jest funkcja przełączenia się na dowolnego użytkownika (bez weryfikacji), jednak to odpada, bo musimy weryfikować, na kogo.

Najlepiej chyba byłoby to zrobić tak, że tworzymy endpoint np. /switch i wysyłamy nagłówek Authorization: Bearer z tokenem JWT. Problem w tym, że wtedy Spring Security tego nie rozumie i wysyła odpowiedź Unauthenticated. Być może trzeba napisać filtr lub wykorzystać istniejący, żeby dla tego konkretnego endpointu był sprawdzany token JWT, a potem go ręcznie generować. Ktoś wie, jak to ugryźć i czy Spring Security ma coś takiego?

Shalom
  • Rejestracja:około 21 lat
  • Ostatnio:prawie 3 lata
  • Lokalizacja:Space: the final frontier
  • Postów:26433
0

Ja chyba nie całkiem rozumiem co chcesz zrobić. Chcesz wysłać request zawierający JWT i wygenerować nowe JWT w odpowiedzi?


"Nie brookliński most, ale przemienić w jasny, nowy dzień najsmutniejszą noc - to jest dopiero coś!"
SI
  • Rejestracja:ponad 3 lata
  • Ostatnio:14 dni
  • Postów:62
0

@Shalom: Tak. Chcę wysłać request zawierający JWT i wygenerować w odpowiedzi nowe JWT (dla innego konta, które należy do tego samego właściciela).

B9
  • Rejestracja:ponad 7 lat
  • Ostatnio:2 dni
  • Postów:65
0

Nie wiem czy tak to ma wyglądać, ale klikasz sobie załóżmy w swój awatar i wyświetlają ci się powiązane konta. Klikając na dane konto w celu przełączenia, leci ci na jakiś endpoint do przełączania konta. Wymagane jest, aby endpoint był dostępny dla zalogowanych użytkowników, nie ważne z jakimi uprawnieniami. Sprawdza aktualny token + przesłane informacje na jakie konto użytkownik chce się przełączyć. Jeżeli konto jest powiązane to tworzy nowy token i odsyła użytkownikowi. Nie potrzebujesz hasła, ani nic, gdyż masz uniwersalne hasło do tworzenia tokenów.

Drugą opcją, aby w locie to było robione, to użycie filtra. Tutaj jednak zależy co chciałbyś osiągnąć. Jeżeli dane usługi dostępne są tylko dla osób z odpowiednimi uprawnieniami, to byłoby automatycznie przełączenie na powiązane konto. Jednak pomysł z endpointem bardziej mi się podoba.

Nie wiem czy o to ci chodziło

SI
  • Rejestracja:ponad 3 lata
  • Ostatnio:14 dni
  • Postów:62
0

Pierwszy pomysł. Tylko czy da się to zrobić tak, jak bym chciał?

Jeśli w nagłówku Authorization zamiast uwierzytelnienia klienta (Basic authentication ze sztywnym loginem i hasłem) pójdzie token JWT zalogowanego użytkownika, to Spring Security z automatu odeśle WWW-Authenticate i przeglądarka wyświetli monit o login i hasło (nie użytkownika, tylko klienta, czyli aplikacji webowej). Pewnie da się to ominąć filtrami, czyli że dla konkretnego endpointu stosujemy inny filtr, który z nagłówka Authorization odczytuje token JWT użytkownika albo nie sprawdza tego nagłówka wcale i trzeba to ręcznie zrobić (sparsować token, zweryfikować użytkownika i wygenerować nowy token). Inny sposób to przekazać JWT jako parametr query string i też ręcznie robić weryfikację. Aż dziwne, że Spring Security nie ma tego out of box.

Jeśli znacie jakieś przykłady w istniejących aplikacjach, jak sensownie do tego podejść, to podeślijcie.

Druga kwestia to "zapamiętaj mnie". Jeśli generujemy JWT na podstawie loginu i hasła, to nie a się w ogóle generować refresh tokenów - jest to zablokowane w Springu, rzekomo tak nakazuje specyfikacja OAuth. Dlaczego tak jest?

B9
  • Rejestracja:ponad 7 lat
  • Ostatnio:2 dni
  • Postów:65
0

Jeżeli twoja aplikacja to rest, czyli udostępnia zasoby i przyjmuje (jest bezstanowa), to nie powinna się opierać tylko na jwt, a nie remember me?

SI
  • Rejestracja:ponad 3 lata
  • Ostatnio:14 dni
  • Postów:62
0

@brus97: tak, opiera się na REST, ale token JWT ma datę ważności i bez odświeżenia (np. refresh tokenem, który nie przychodzi) token JWT traci ważność po pewnym czasie. Oczywiście można ustawić datę ważności na bardzo odległą, tylko na ile to bezpieczne? Lepiej chyba odświeżać go co jakiś czas (czyli generować nowy).

A problem z przełączaniem da się łatwo obejść, trzymając login i hasło w aplikacji (np. w sessionStorage) i przy przełączaniu użytkownika po prostu logować się tymi samymi danymi, ale dodatkowo przekazując ID innego konta. I wtedy nie trzeba by było pisać dodatkowych filtrów, jeśli Spring Security czegoś takiego nie posiada. Tylko jest tu kwestia bezpieczeństwa, żeby trzymać wrażliwe dane w pamięci.

B9
  • Rejestracja:ponad 7 lat
  • Ostatnio:2 dni
  • Postów:65
0

Nie nie, przy rest nie trzymasz żadnych danych logowania w aplikacji. Żeby wykonać jwt w springu (takim najprostszym sposobem), implementujesz mechanizm logowania jaki chcesz (możesz przy pomocy UserDetails i UserDetailsService) na endpoint /login. Przeprowadza on autentykacje i zwraca jwt, po czym każdy request ma dołączony ten jwt i przechodzi on przez filtr, który sam definiujesz. Spring nie ma wbudowanego security na podstawie jwt (z tego co mi wiadomo). Jest wiele bibliotek, które pomagają to obsłużyć. Żeby przelogować użytkownika, robisz sobie osobny endpoint. Dla refresh tokenu też osobny. Jest taka metoda dla HttpSecurity, addFilter(...). Dzięki niej możesz ustalić co ma się przed danym filtrem wykonać, lub po.

Aplikacja webowa jeżeli dostaje exception unauthorized, sprawdza czy ma refresh token i wysyła go na np. /refresh. Wtedy następuje wygenerowanie nowego. Pamiętaj, że tylko prawdziwy token, ma gwarantować dostęp do zasobów, dlatego ma on krótki czas.

SI
  • Rejestracja:ponad 3 lata
  • Ostatnio:14 dni
  • Postów:62
0

@brus97: Tak, opiera się na JWT (który zawiera ID użytkownika i datę ważności). Kwestia zapamiętywania - z tego, co wiem, to uderzamy refresh tokenem do serwera autentykacyjnego, tyle że w Springu refresh tokeny są zablokowane, jeśli uwierzytelniamy się loginem i hasłem.

Ale temat jest o przełączaniu konta użytkownika, czyli jak zmusić Spring Security, żeby odczytał z JWT token istniejącego użytkownika i zwrócił token JWT dla tego, na którego chcemy się przełączyć. Jeśli nie da się tego zrobić w prosty sposób, no to w takim razie trzeba napisać własny filtr.

Charles_Ray
  • Rejestracja:około 17 lat
  • Ostatnio:około 2 godziny
  • Postów:1873
0

Przełączenie konta == logout + login? 😅


”Engineering is easy. People are hard.” Bill Coughran
SI
  • Rejestracja:ponad 3 lata
  • Ostatnio:14 dni
  • Postów:62
0

@Charles_Ray: Pod warunkiem, że zapamiętamy w aplikacji login i hasło użytkownika, co nie jest bezpieczne. Wystarczy atak XSS i wykonanie

Kopiuj
sessionStorage.get('password')

Z drugiej strony gdzieś trzeba też trzymać token JWT i na razie ładuję go do sessionStorage. Też błąd. Podobno zaleca się ciastka z httpOnly. Ale wtedy trzeba by zmienić sposób wysyłki JWT, nie w nagłówku Authorization (tak to chyba działa w większości apek webowych), tylko w ciastku. A czy jest bezpieczniejsza alternatywa?

Jako load balancer stoi Envoy i on też umie czytać JWT z ciastek.

edytowany 1x, ostatnio: Shiba Inu
Charles_Ray
  • Rejestracja:około 17 lat
  • Ostatnio:około 2 godziny
  • Postów:1873
0

W gmailu/youtubie jak przełączasz konto też możesz zostać poproszony o uwierzytelnienie się. Ciekawe co się dzieje z ciastkiem po przełączeniu konta, może dostaje się nowe?

Sama lista kont (dane dla komponentu na UI) może spokojnie być zapisywana w local storage.

Raczej koncept powiązanych kont to jest coś, co będziesz musiał zakodzić sam.


”Engineering is easy. People are hard.” Bill Coughran
edytowany 1x, ostatnio: Charles_Ray
piotrpo
  • Rejestracja:ponad 7 lat
  • Ostatnio:3 dni
  • Postów:3277
0

Użytkownik != konto
Możesz zrobić tak, że serwis wystawiający JWT umieszcza w nim informację do jakich kont token ma zapewnić dostęp:

Logujesz się jako Adam
dostajesz JWT z jakimś tam id użytkownika i dopisanym "Adam"

Używając logujesz się jako Bartek, ale jednocześnie przesyłając JWT "Adam"
Dostajesz JWT z informacją o dostępie do kont "Adam", "Bartek"

Każde odświeżenie tokenu zwraca ci nowy JWT zawierający informację o wszystkich kontach do których jesteś zalogowany

Tak stworzonego tokenu używasz do autoryzacji dostępu do zasobu, w dodatkowym nagłówku wysyłając aktualne alter ego.

Kliknij, aby dodać treść...

Pomoc 1.18.8

Typografia

Edytor obsługuje składnie Markdown, w której pojedynczy akcent *kursywa* oraz _kursywa_ to pochylenie. Z kolei podwójny akcent **pogrubienie** oraz __pogrubienie__ to pogrubienie. Dodanie znaczników ~~strike~~ to przekreślenie.

Możesz dodać formatowanie komendami , , oraz .

Ponieważ dekoracja podkreślenia jest przeznaczona na linki, markdown nie zawiera specjalnej składni dla podkreślenia. Dlatego by dodać podkreślenie, użyj <u>underline</u>.

Komendy formatujące reagują na skróty klawiszowe: Ctrl+B, Ctrl+I, Ctrl+U oraz Ctrl+S.

Linki

By dodać link w edytorze użyj komendy lub użyj składni [title](link). URL umieszczony w linku lub nawet URL umieszczony bezpośrednio w tekście będzie aktywny i klikalny.

Jeżeli chcesz, możesz samodzielnie dodać link: <a href="link">title</a>.

Wewnętrzne odnośniki

Możesz umieścić odnośnik do wewnętrznej podstrony, używając następującej składni: [[Delphi/Kompendium]] lub [[Delphi/Kompendium|kliknij, aby przejść do kompendium]]. Odnośniki mogą prowadzić do Forum 4programmers.net lub np. do Kompendium.

Wspomnienia użytkowników

By wspomnieć użytkownika forum, wpisz w formularzu znak @. Zobaczysz okienko samouzupełniające nazwy użytkowników. Samouzupełnienie dobierze odpowiedni format wspomnienia, zależnie od tego czy w nazwie użytkownika znajduje się spacja.

Znaczniki HTML

Dozwolone jest używanie niektórych znaczników HTML: <a>, <b>, <i>, <kbd>, <del>, <strong>, <dfn>, <pre>, <blockquote>, <hr/>, <sub>, <sup> oraz <img/>.

Skróty klawiszowe

Dodaj kombinację klawiszy komendą notacji klawiszy lub skrótem klawiszowym Alt+K.

Reprezentuj kombinacje klawiszowe używając taga <kbd>. Oddziel od siebie klawisze znakiem plus, np <kbd>Alt+Tab</kbd>.

Indeks górny oraz dolny

Przykład: wpisując H<sub>2</sub>O i m<sup>2</sup> otrzymasz: H2O i m2.

Składnia Tex

By precyzyjnie wyrazić działanie matematyczne, użyj składni Tex.

<tex>arcctg(x) = argtan(\frac{1}{x}) = arcsin(\frac{1}{\sqrt{1+x^2}})</tex>

Kod źródłowy

Krótkie fragmenty kodu

Wszelkie jednolinijkowe instrukcje języka programowania powinny być zawarte pomiędzy obróconymi apostrofami: `kod instrukcji` lub ``console.log(`string`);``.

Kod wielolinijkowy

Dodaj fragment kodu komendą . Fragmenty kodu zajmujące całą lub więcej linijek powinny być umieszczone w wielolinijkowym fragmencie kodu. Znaczniki ``` lub ~~~ umożliwiają kolorowanie różnych języków programowania. Możemy nadać nazwę języka programowania używając auto-uzupełnienia, kod został pokolorowany używając konkretnych ustawień kolorowania składni:

```javascript
document.write('Hello World');
```

Możesz zaznaczyć również już wklejony kod w edytorze, i użyć komendy  by zamienić go w kod. Użyj kombinacji Ctrl+`, by dodać fragment kodu bez oznaczników języka.

Tabelki

Dodaj przykładową tabelkę używając komendy . Przykładowa tabelka składa się z dwóch kolumn, nagłówka i jednego wiersza.

Wygeneruj tabelkę na podstawie szablonu. Oddziel komórki separatorem ; lub |, a następnie zaznacz szablonu.

nazwisko;dziedzina;odkrycie
Pitagoras;mathematics;Pythagorean Theorem
Albert Einstein;physics;General Relativity
Marie Curie, Pierre Curie;chemistry;Radium, Polonium

Użyj komendy by zamienić zaznaczony szablon na tabelkę Markdown.

Lista uporządkowana i nieuporządkowana

Możliwe jest tworzenie listy numerowanych oraz wypunktowanych. Wystarczy, że pierwszym znakiem linii będzie * lub - dla listy nieuporządkowanej oraz 1. dla listy uporządkowanej.

Użyj komendy by dodać listę uporządkowaną.

1. Lista numerowana
2. Lista numerowana

Użyj komendy by dodać listę nieuporządkowaną.

* Lista wypunktowana
* Lista wypunktowana
** Lista wypunktowana (drugi poziom)

Składnia Markdown

Edytor obsługuje składnię Markdown, która składa się ze znaków specjalnych. Dostępne komendy, jak formatowanie , dodanie tabelki lub fragmentu kodu są w pewnym sensie świadome otaczającej jej składni, i postarają się unikać uszkodzenia jej.

Dla przykładu, używając tylko dostępnych komend, nie możemy dodać formatowania pogrubienia do kodu wielolinijkowego, albo dodać listy do tabelki - mogłoby to doprowadzić do uszkodzenia składni.

W pewnych odosobnionych przypadkach brak nowej linii przed elementami markdown również mógłby uszkodzić składnie, dlatego edytor dodaje brakujące nowe linie. Dla przykładu, dodanie formatowania pochylenia zaraz po tabelce, mogłoby zostać błędne zinterpretowane, więc edytor doda oddzielającą nową linię pomiędzy tabelką, a pochyleniem.

Skróty klawiszowe

Skróty formatujące, kiedy w edytorze znajduje się pojedynczy kursor, wstawiają sformatowany tekst przykładowy. Jeśli w edytorze znajduje się zaznaczenie (słowo, linijka, paragraf), wtedy zaznaczenie zostaje sformatowane.

  • Ctrl+B - dodaj pogrubienie lub pogrub zaznaczenie
  • Ctrl+I - dodaj pochylenie lub pochyl zaznaczenie
  • Ctrl+U - dodaj podkreślenie lub podkreśl zaznaczenie
  • Ctrl+S - dodaj przekreślenie lub przekreśl zaznaczenie

Notacja Klawiszy

  • Alt+K - dodaj notację klawiszy

Fragment kodu bez oznacznika

  • Alt+C - dodaj pusty fragment kodu

Skróty operujące na kodzie i linijkach:

  • Alt+L - zaznaczenie całej linii
  • Alt+, Alt+ - przeniesienie linijki w której znajduje się kursor w górę/dół.
  • Tab/⌘+] - dodaj wcięcie (wcięcie w prawo)
  • Shit+Tab/⌘+[ - usunięcie wcięcia (wycięcie w lewo)

Dodawanie postów:

  • Ctrl+Enter - dodaj post
  • ⌘+Enter - dodaj post (MacOS)