Tavaszi WebClient és OAuth2 támogatás

1. Áttekintés

A Spring Security 5 biztosítja az OAuth2 támogatást a Spring Webflux nem blokkolásához Web Ügyfél osztály.

Ebben az oktatóanyagban különböző megközelítéseket fogunk elemezni a biztonságos erőforrások eléréséhez ezen osztály használatával.

Ezenkívül megnézzük a motorháztető alatt, hogy megértsük, hogy Spring hogyan kezeli az OAuth2 engedélyezési folyamatot.

2. A forgatókönyv beállítása

Az OAuth2 specifikációval összhangban, kliensünkön kívül - amely a cikkünk fő témája - természetesen szükségünk van egy hitelesítési kiszolgálóra és egy erőforrás-kiszolgálóra.

Használhatunk olyan ismert hitelesítés-szolgáltatókat, mint a Google vagy a Github. Az OAuth2 kliens szerepének jobb megértése érdekében saját szervereinket is használhatjuk, itt elérhető megvalósítással. Nem mutatjuk meg a teljes konfigurációt, mivel ez nem az oktatóanyag témája, elég, ha tudjuk:

  • az Authorization Server a következő lesz:
    • fut a kikötőn 8081
    • kitéve a / oauth / engedélyez,/ oauth / token és oauth / check_token végpontok a kívánt funkcionalitás végrehajtásához
    • mintahasználókkal konfigurálva (pl. János/123) és egyetlen OAuth kliens (fooClientIdPassword/titok)
  • az erőforrás-kiszolgáló el lesz különítve a Hitelesítési szervertől, és:
    • fut a kikötőn 8082
    • szolgáló egy egyszerű Foo objektummal biztosított erőforrás, amely a / foos / {id} végpont

Megjegyzés: fontos megérteni, hogy számos tavaszi projekt különböző OAuth-funkciókat és megvalósításokat kínál. Megvizsgálhatjuk, hogy az egyes könyvtárak mit nyújtanak ebben a Spring Projects mátrixban.

A Web Ügyfél és az összes reaktív Webflux-tal kapcsolatos funkció a Spring Security 5 projekt része. Ezért főleg ezt a keretrendszert fogjuk használni a cikkben.

3. Tavaszi biztonság 5 A motorháztető alatt

Az előttünk álló példák teljes megértése érdekében jó tudni, hogy a Spring Security miként kezeli az OAuth2 szolgáltatásait belsőleg.

Ez a keret a következő lehetőségeket kínálja:

  • támaszkodhat egy OAuth2 szolgáltatói fiókra a felhasználók bejelentkezéséhez az alkalmazásba
  • konfigurálja szolgáltatásunkat OAuth2 kliensként
  • kezelje az engedélyezési eljárásokat számunkra
  • a tokenek automatikus frissítése
  • tárolja a hitelesítő adatokat, ha szükséges

A Spring Security OAuth2-világának néhány alapvető fogalmát a következő ábra ismerteti:

3.1. Szolgáltatók

Spring meghatározza az OAuth2 szolgáltató szerepkört, amely felelős az OAuth 2.0 védett erőforrások feltárásáért.

Példánkban a Hitelesítési szolgáltatásunk fogja felajánlani a Szolgáltató képességeit.

3.2. Ügyfélregisztrációk

A ClientRegistration olyan entitás, amely egy OAuth2 (vagy OpenID) szolgáltatóba bejegyzett adott ügyfél összes releváns információját tartalmazza.

A mi esetünkben ez lesz a Hitelesítési kiszolgálón regisztrált kliens, amelyet a bael-client-id id.

3.3. Meghatalmazott ügyfelek

Amint a végfelhasználó (más néven az erőforrás-tulajdonos) engedélyeket ad az ügyfélnek az erőforrásokhoz való hozzáféréshez, egy OAuth2AuthorizedClient entitás jön létre.

Felelős lesz a hozzáférési tokenek társításáról az ügyfelek regisztrációihoz és az erőforrástulajdonosokhoz (képviseli: tárgyak).

3.4. Adattárak

Ezenkívül a Spring Security tárházi osztályokat is kínál a fent említett entitások eléréséhez.

Különösen a ReactiveClientRegistrationRepository és a ServerOAuth2AuthorizedClientRepository osztályokat reaktív halmokban használják, és alapértelmezés szerint a memóriában tárolják.

A Spring Boot 2.x létrehozza ezeknek az adattárosztályoknak a babjait, és automatikusan hozzáadja azokat a kontextushoz.

3.5. Biztonsági webszűrő lánc

A Spring Security 5 egyik kulcsfogalma a reaktív SecurityWebFilterChain entitás.

Ahogy a neve is jelzi, a WebFilter tárgyakat.

Amikor engedélyezzük az OAuth2 szolgáltatásokat alkalmazásunkban, a Spring Security két szűrőt ad hozzá a lánchoz:

  1. Egy szűrő válaszol az engedélyezési kérelmekre (a / oauth2 / authorisation / {registrationId} URI) vagy dob a ClientAuthorizationRequiredException. Hivatkozást tartalmaz a ReactiveClientRegistrationRepository, és ő felel a felhasználói ügynök átirányításának engedélyezési kérelméért.
  2. A második szűrő attól függ, hogy melyik funkciót adjuk hozzá (OAuth2 Client képességek vagy az OAuth2 Login funkció). Mindkét esetben a szűrő fő feladata a OAuth2AuthorizedClient és tárolja a ServerOAuth2AuthorizedClientRepository.

3.6. Web kliens

A webes klienst egy ExchangeFilterFunction a tárhelyekre való hivatkozásokat tartalmazó.

Ezeket fogja használni a hozzáférési token megszerzéséhez, hogy automatikusan hozzáadja a kérelemhez.

4. Spring Security 5 támogatás - az ügyfél hitelesítő adatainak folyamata

A Spring Security lehetővé teszi alkalmazásunk OAuth2 kliensként történő konfigurálását.

Ebben az írásban a Web Ügyfél példány az erőforrások lekéréséhez az „Ügyfél-hitelesítő adatok” használatávalelőször a támogatás típusát, majd az „Engedélyezési kód” folyamatot használja.

Az első dolog, amit tennünk kell, az az ügyfél-regisztráció és a szolgáltató konfigurálása, amelyet a hozzáférési token megszerzéséhez használunk.

4.1. Ügyfél és szolgáltató konfigurációi

Amint azt az OAuth2 bejelentkezés cikkében láthattuk, vagy programozottan konfigurálhatjuk, vagy támaszkodhatunk a tavaszi indítás automatikus konfigurálására a tulajdonságok használatával a regisztrációnk meghatározásához:

spring.security.oauth2.client.registration.bael.authorization-grant-type = client_credentials spring.security.oauth2.client.registration.bael.client-id = bael-client-id spring.security.oauth2.client.registry. bael.client-secret = bael-secret spring.security.oauth2.client.provider.bael.token-uri = // localhost: 8085 / oauth / token

Ezek mind azok a konfigurációk, amelyekre az erőforrás lekéréséhez szükségünk van a client_credentials folyam.

4.2. Használni a Web Ügyfél

Ezt a támogatási típust gép-gép kommunikációban használjuk, ahol nincs végfelhasználó interakció az alkalmazásunkkal.

Képzeljük el például, hogy van egy cron olyan feladat, amely megpróbál egy biztonságos erőforrást megszerezni a Web Ügyfél alkalmazásunkban:

@Autowired private WebClient webClient; @Scheduled (fixedRate = 5000) public void logResourceServiceResponse () {webClient.get () .uri ("// localhost: 8084 / retrieve-resource") .retrieve () .bodyToMono (String.class) .map (karakterlánc -> "Az ügyfél hitelesítő adataival megadva Grant Type:" + string ".subscribe (logger :: info); }

4.3. A. Konfigurálása Web Ügyfél

Ezután állítsuk be a web Ügyfél például, hogy automatikusan beküldtük az ütemezett feladatot:

@Bean WebClient webClient (ReactiveClientRegistrationRepository clientRegistrations) {ServerOAuth2AuthorizedClientExchangeFilterFunction oauth = new ServerOAuth2AuthorizedClientExchangeFilterFunction (clientRegistrations, new UnAuthenticatedServerOAut2Aut; oauth.setDefaultClientRegistrationId ("bael"); return WebClient.builder () .filter (oauth) .build (); }

Mint mondtuk, a kliens regisztrációs adattárat a Spring Boot automatikusan létrehozza és hozzáadja a kontextushoz.

A következő dolog, amit itt észre kell venni, hogy a UnAuthenticatedServerOAuth2AuthorizedClientRepository példa. Ez annak a ténynek köszönhető, hogy egyetlen végfelhasználó sem vesz részt a folyamatban, mivel ez gép-gép kommunikáció. Végül kijelentettük, hogy a bael alapértelmezés szerint az ügyfél regisztrációja.

Ellenkező esetben meg kell adnunk, mire meghatározzuk a kérést a cron jobban:

webClient.get () .uri ("// localhost: 8084 / retrieve-resource") .attribútumok (ServerOAuth2AuthorizedClientExchangeFilterFunction .clientRegistrationId ("bael")) .retrieve () // ...

4.4. Tesztelés

Ha az alkalmazásunkat a HIBÁK a naplózási szint engedélyezve, láthatjuk a Spring Security általunk végzett hívásokat:

oswrfclient.ExchangeFunctions: HTTP POST // localhost: 8085 / oauth / token oshttp.codec.json.Jackson2JsonDecoder: dekódolt [{access_token = 89cf72cd-183e-48a8-9d08-661584db43_, = 4 read (csonka) ...] oswrfclient.ExchangeFunctions: HTTP GET // localhost: 8084 / retrieve-resource oscore.codec.StringDecoder: Dekódolt "Ez az erőforrás!" c.b.w.c.service.WebClientChonJob: A következő erőforrást a Client Credentials Grant Type használatával kaptuk meg: Ez az erőforrás!

Azt is észrevesszük, hogy a feladat második futtatásakor az alkalmazás anélkül kéri az erőforrást, hogy először tokent kérne, mivel az utolsó még nem járt le.

5. Tavaszi biztonság 5 Támogatás - megvalósítás az engedélyezési kódfolyamat használatával

Ezt a támogatási típust általában olyan esetekben használják, amikor a kevésbé megbízható, harmadik féltől származó alkalmazásoknak erőforrásokhoz kell hozzáférniük.

5.1. Ügyfél és szolgáltató konfigurációi

Az OAuth2 folyamat végrehajtásához az Engedélyezési kód folyamat használatával még több tulajdonságot kell megadnunk kliens regisztrációnk és a szolgáltató számára:

spring.security.oauth2.client.registration.bael.client-name = bael spring.security.oauth2.client.registration.bael.client-id = bael-client-id spring.security.oauth2.client.registration.bael. kliens-titok = bael-titkos spring.security.oauth2.client.registration.bael .authorization-grant-type = engedélyezési kód spring.security.oauth2.client.registration.bael .redirect-uri = // localhost: 8080 / login / oauth2 / code / bael spring.security.oauth2.client.provider.bael.token-uri = // localhost: 8085 / oauth / token spring.security.oauth2.client.provider.bael .authorization-uri = // localhost: 8085 / oauth / engedélyezze a spring.security.oauth2.client.provider.bael.user-info-uri = // localhost: 8084 / user spring.security.oauth2.client.provider.bael.user-name-attribute = name

Az előző szakaszban használt tulajdonságokon kívül ezúttal a következőket is tartalmaznunk kell:

  • Végpont, amelyet hitelesíteni kell a Hitelesítési kiszolgálón
  • Felhasználói információkat tartalmazó végpont URL-je
  • Az alkalmazásunk azon végpontjának URL-je, amelyre a felhasználói ügynököt hitelesítés után átirányítják

Természetesen a jól ismert szolgáltatók esetében az első két pontot nem kell meghatározni.

Az átirányítási végpontot a Spring Security automatikusan létrehozza.

Alapértelmezés szerint a hozzá konfigurált URL az / [action] / oauth2 / code / [registrationId], csak engedélyezzék és Belépés megengedett műveletek (a végtelen hurok elkerülése érdekében).

Ez a végpont felelős a következőkért:

  • megkapja a hitelesítési kódot lekérdezési paraméternek
  • felhasználása hozzáférési token megszerzésére
  • az Authorized Client példány létrehozása
  • átirányítja a felhasználói ügynököt az eredeti végpontra

5.2. HTTP biztonsági konfigurációk

Ezután konfigurálnunk kell a SecurityWebFilterChain.

A leggyakoribb forgatókönyv a Spring Security OAuth2 bejelentkezési képességeinek használata a felhasználók hitelesítéséhez, és hozzáférés biztosításához számukra a végpontjainkhoz és erőforrásainkhoz.

Ha ez a mi esetünk, akkor csak a oauth2Login irányelv ServerHttpSecurity meghatározás elég lesz ahhoz, hogy alkalmazásunk OAuth2 kliensként is működjön:

@Bean public SecurityWebFilterChain springSecurityFilterChain (ServerHttpSecurity http) {http.authorizeExchange () .anyExchange () .hitelesített () .és () .oauth2Login (); return http.build (); }

5.3. A. Konfigurálása Web Ügyfél

Itt az ideje, hogy a helyünkre tegyük Web Ügyfél példa:

@Bean WebClient webClient (ReactiveClientRegistrationRepository clientRegistrations, ServerOAuth2AuthorizedClientRepository AuthorClients) {ServerOAuth2AuthorizedClientExchangeFilterFunction oauth = új ServerOAuth2AuthorizedClientExchangeFilterFunction oauth.setDefaultOAuth2AuthorizedClient (true); return WebClient.builder () .filter (oauth) .build (); }

Ezúttal a kliens regisztráció és az engedélyezett kliens adattárat injektáljuk a kontextusból.

Engedélyezzük a setDefaultOAuth2AuthorizedClient választási lehetőség. Ezzel a keretrendszer megpróbálja megszerezni az ügyfélinformációkat az aktuálisból Hitelesítés a Spring Security által kezelt objektum.

Figyelembe kell vennünk, hogy ezzel együtt az összes HTTP kérés tartalmazza a hozzáférési tokent, ami nem biztos, hogy a kívánt viselkedés.

Később elemezzük az alternatívákat, hogy jelezzük az ügyfélnek, hogy egy adott Web Ügyfél tranzakció fogja használni.

5.4. Használni a Web Ügyfél

A jogosultsági kódhoz olyan felhasználói ügynökre van szükség, amely átirányításokat (pl. Böngészőt) tud kidolgozni az eljárás végrehajtásához.

Ezért ezt a támogatási típust használjuk, amikor a felhasználó interakcióba lép az alkalmazásunkkal, általában HTTP-végpontot hívva:

@RestController public class ClientRestController {@Autowired WebClient webClient; @GetMapping ("/ auth-code") Mono useOauthWithAuthCode () {Mono retrievedResource = webClient.get () .uri ("// localhost: 8084 / retrieve-resource") .retrieve () .bodyToMono (String.osztály); return retrievedResource.map (string -> "A következő erőforrást az Oauth használatával kaptuk le:" + string); }}

5.5. Tesztelés

Végül felhívjuk a végpontot, és a naplóbejegyzések ellenőrzésével elemezzük, mi történik.

Miután meghívtuk a végpontot, az alkalmazás ellenőrzi, hogy még nem vagyunk hitelesítve az alkalmazásban:

o.s.w.s.adapter.HttpWebHandlerAdapter: HTTP GET "/ auth-code" ... HTTP / 1.1 302 Talált hely: / oauth2 / authorisation / bael

Az alkalmazás átirányítja a Hitelesítési szolgáltatás végpontjába, hogy hitelesítsen a Szolgáltató nyilvántartásaiban található hitelesítő adatokkal (esetünkben a bael-user / bael-password):

HTTP / 1.1 302 Talált hely: // localhost: 8085 / oauth / authorize? Response_type = kód & client_id = bael-client-id & state = ... & redirect_uri = http% 3A% 2F% 2Flocalhost% 3A8080% 2Flogin% 2Foauth2% 2Fcode% 2Fbael

A hitelesítést követően a felhasználói ügynököt visszaküldik az Átirányítási URI-re, a kóddal mint lekérdezési paraméternek és az első állapotértékkel (a CSRF-támadások elkerülése érdekében):

o.s.w.s.adapter.HttpWebHandlerAdapter: HTTP GET "/ login / oauth2 / code / bael? code = ... & state = ...

Ezután az alkalmazás a kód segítségével megszerzi a hozzáférési tokent:

o.s.w.r.f.client.ExchangeFunctions: HTTP POST // localhost: 8085 / oauth / token

Felhasználói információkat szerez:

o.s.w.r.f.client.ExchangeFunctions: HTTP GET // localhost: 8084 / felhasználó

És átirányítja a felhasználói ügynököt az eredeti végpontra:

HTTP / 1.1 302 Talált hely: / auth-code

Végül a mi Web Ügyfél példány sikeresen kérheti a biztonságos erőforrást:

o.s.w.r.f.client.ExchangeFunctions: HTTP GET // localhost: 8084 / retrieve-resource o.s.w.r.f.client.ExchangeFunctions: Válasz 200 OK o.s.core.codec.StringDecoder: Dekódolt "Ez az erőforrás!"

6. Alternatíva - Ügyfél regisztráció a felhívásban

Korábban láttuk, hogy a setDefaultOAuth2AuthorizedClientazt jelenti, hogy az alkalmazás a hozzáférési jogkivonatot tartalmazza az ügyféllel folytatott minden hívásban.

Ha eltávolítjuk ezt a parancsot a konfigurációból, akkor pontosan meg kell határoznunk az ügyfél regisztrációját, mire meghatározzuk a kérést.

Az egyik mód természetesen a clientRegistrationId mint korábban, amikor az ügyfél hitelesítő adatfolyamában dolgoztunk.

Mivel társítottuk a meghatalmazott ügyfelekkel megszerezhetjük a OAuth2AuthorizedClient például a @ RegisteredOAuth2AuthorizedClient kommentár:

@GetMapping ("/ auth-code-annotated") Egyszerű használat attribútumok (ServerOAuth2AuthorizedClientExchangeFilterFunction.oauth2AuthorizedClient (AuthorizedClient)) .retrieve () .bodyToMono (String.class); return retrievedResource.map (karakterlánc -> "Erőforrás:" + karakterlánc + "- Fő társítva:" + authorisedClient.getPrincipalName () + "- A token lejár: }

7. Kerülje az OAuth2 bejelentkezési funkciókat

Mint mondtuk, a leggyakoribb forgatókönyv az OAuth2 jogosultság-szolgáltatóra hagyatkozik, hogy bejelentkezzen a felhasználókba alkalmazásunkban.

De mi van, ha ezt el akarjuk kerülni, de mégis hozzáférhetünk a biztonságos erőforrásokhoz az OAuth2 protokoll segítségével? Ezután változtatnunk kell a konfigurációnkon.

Kezdőknek, és csak azért, hogy egyértelműek legyünk az egészben, használhatjuk a engedélyezzék akció helyett Belépés egyet az átirányítás URI tulajdonságának meghatározásakor:

spring.security.oauth2.client.registration.bael .redirect-uri = // localhost: 8080 / login / oauth2 / code / bael

A felhasználóhoz kapcsolódó tulajdonságokat el is dobhatjuk, mivel nem a létrehozásukhoz használjuk őket alkalmazásunkban.

Most konfiguráljuk a SecurityWebFilterChain anélkül, hogy a oauth2Login parancsot, és helyette felvesszük a oauth2Client egy.

Annak ellenére, hogy nem akarunk az OAuth2 bejelentkezésre hagyatkozni, mégis hitelesíteni szeretnénk a felhasználókat, mielőtt hozzáférnénk a végpontunkhoz. Emiatt a formLogin irányelv itt:

@Bean public SecurityWebFilterChain springSecurityFilterChain (ServerHttpSecurity http) {http.authorizeExchange () .anyExchange () .authenticated () .and () .oauth2Client () .and () .formLogin (); return http.build (); }

Futtassuk most az alkalmazást, és nézzük meg, mi történik, amikor az alkalmazást használjuk / auth-code-annotated végpont.

Először a bejelentkezési űrlappal kell bejelentkeznünk alkalmazásunkba.

Ezután az alkalmazás átirányít minket az Engedélyezési szolgáltatás bejelentkezésére, hogy hozzáférést biztosítson erőforrásainkhoz.

Megjegyzés: miután ezt megtette, vissza kell irányítani az eredeti végpontra, amelyet hívtunk. Ennek ellenére a Spring Security úgy tűnik, hogy inkább a „/” gyökérútra irányít vissza, ami hibának tűnik. A következő kérések az OAuth2 táncot kiváltó után sikeresen futnak.

A végpont válaszában láthatjuk, hogy az engedélyezett ügyfél ezúttal egy megnevezett főhöz van társítva bael-client-id a helyett bael-user, nevét a Hitelesítési szolgáltatásban konfigurált felhasználóról kapta.

8. Tavaszi keret támogatás - kézi megközelítés

A dobozból, Az 5. tavasz csak egy OAuth2-vel kapcsolatos szolgáltatási módszert kínál, hogy könnyedén hozzáadhasson egy Bearer token fejlécet a kéréshez. Ez a HttpHeaders # setBearerAuth módszer.

Most látunk egy példát, hogy megértsük, mi kell a biztonságos erőforrás megszerzéséhez egy OAuth2 tánc manuális előadásával.

Egyszerűen fogalmazva két HTTP kérést kell láncolnunk: az egyiket, hogy hitelesítési tokent kapjunk az Authorization Server-től, a másikat pedig az erőforrás megszerzéséhez ezzel a tokennel:

@Autowired WebClient kliens; public Mono getSecuredResource () {String encodedClientData = Base64Utils.encodeToString ("bael-client-id: bael-secret" .getBytes ()); Mono erőforrás = client.post () .uri ("localhost: 8085 / oauth / token") .header ("Engedélyezés", "Alap" + encodedClientData) .body (BodyInserters.fromFormData ("grant_type", "client_credentials")) .retrieve () .bodyToMono (JsonNode.class) .flatMap (tokenResponse -> {String accessTokenValue = tokenResponse.get ("access_token") .textValue (); return client.get () .uri ("localhost: 8084 / retrieve- erőforrás ") .fejlécek (h -> h.setBearerAuth (accessTokenValue)) .retrieve () .bodyToMono (String.osztály);}); return resource.map (res -> "Az erőforrás kézi megközelítéssel történt:" + res); }

Ez a példa főleg annak megértésére szolgál, hogy mennyire nehézkes lehet az OAuth2 specifikációt követő kérelem kihasználása, és annak megértése, hogy a setBearerAuth módszert alkalmaznak.

Valós életben azt hagytuk, hogy a Spring Security átlátható módon gondoskodjon a számunkra végzett kemény munkáról, ahogyan azt az előző szakaszokban is megtettük.

9. Következtetés

Ebben az oktatóanyagban láthattuk, hogyan állíthatjuk be alkalmazásunkat OAuth2 kliensként, és pontosabban hogyan konfigurálhatjuk és használhatjuk az Web Ügyfél egy biztonságos erőforrás lekérése egy teljes reaktív veremben.

Végül, de nem utolsósorban elemeztük, hogy a Spring Security 5 OAuth2 mechanizmusai hogyan működnek a motorháztető alatt, hogy megfeleljenek az OAuth2 specifikációnak.

Mint mindig, a teljes példa elérhető a Github oldalon.