OAuth 2.0 Resursni poslužitelj s proljetnom sigurnošću 5

1. Pregled

U ovom uputstvu naučit ćemo kako postaviti poslužitelj resursa OAuth 2.0 pomoću Spring Security 5.

To ćemo učiniti pomoću JWT-a, kao i neprozirnih tokena, dvije vrste tokena na nosioca koje podržava Spring Security.

Prije nego što prijeđemo na primjere implementacije i koda, uspostavit ćemo neke pozadine.

2. Mala pozadina

2.1. Što su JWT i neprozirni tokeni?

JWT ili JSON Web Token način je za siguran prijenos osjetljivih podataka u široko prihvaćenom JSON formatu. Sadržani podaci mogu se odnositi na korisnika ili na sam token, kao što je njegovo istekanje i izdavač.

S druge strane, neprozirni token, kao što mu samo ime govori, neproziran je u pogledu informacija koje nosi. Token je samo identifikator koji upućuje na podatke pohranjene na autorizacijskom poslužitelju - on se provjerava introspekcijom na kraju poslužitelja.

2.2. Što je poslužitelj resursa?

U kontekstu OAuth 2.0, a resursni poslužitelj je aplikacija koja štiti resurse putem OAuth tokena. Te tokene izdaje autorizacijski poslužitelj, obično za klijentsku aplikaciju. Posao poslužitelja resursa je provjeriti valjanost tokena prije posluživanja resursa klijentu.

Valjanost tokena određuje nekoliko stvari:

  • Je li ovaj token došao s konfiguriranog poslužitelja za autorizaciju?
  • Je li istekao?
  • Je li ovaj poslužitelj resursa predviđena publika?
  • Ima li token potrebno ovlaštenje za pristup traženom resursu?

Da bismo to vizualizirali, pogledajmo dijagram slijeda za tijek autorizacijskog koda i vidimo sve aktere u akciji:

Kao što možemo vidjeti u koraku 8, kada klijentska aplikacija pozove API poslužitelja resursa za pristup zaštićenom resursu, ona prvo ide na autorizacijski poslužitelj da provjeri token sadržan u zahtjevu Odobrenje: Donositelj zaglavlje, a zatim odgovara klijentu.

Korak 9 je ono na što smo se usredotočili u ovom vodiču.

U redu, krenimo sada u dio koda. Postavit ćemo poslužitelj za autorizaciju pomoću Keycloak-a, poslužitelja resursa koji provjerava JWT tokene, drugog poslužitelja resursa koji provjerava neprozirne tokene i nekoliko JUnit testova za simulaciju klijentskih aplikacija i provjeru odgovora.

3. Autorizacijski poslužitelj

Prvo ćemo postaviti autorizacijski poslužitelj ili stvar koja izdaje tokene.

Za to ćemo koristiti Keycloak ugrađen u Spring Boot Application. Keycloak je rješenje za upravljanje identitetom i pristupom otvorenog koda. Budući da smo se u ovom vodiču usredotočili na poslužitelj resursa, nećemo se više upuštati u njega.

Naš ugrađeni poslužitelj Keycloak ima definirana dva klijenta - fooClient i barClient - što odgovara našoj dvije aplikacije poslužitelja resursa.

4. Poslužitelj resursa - Korištenje JWT-ova

Naš poslužitelj resursa imat će četiri glavne komponente:

  • Model - resurs za zaštitu
  • API - REST kontroler za izlaganje resursa
  • Konfiguracija sigurnosti - klasa koja definira kontrolu pristupa za zaštićeni resurs koji API izlaže
  • primjena.iml - konfiguracijska datoteka za deklariranje svojstava, uključujući informacije o autorizacijskom poslužitelju

Pogledajmo ih jednog po jednog za naš poslužitelj resursa koji obrađuje JWT tokene, nakon što zaviri u ovisnosti.

4.1. Ovisnosti Mavena

Uglavnom, trebat će nam spring-boot-starter-oauth2-resource-server, Pokretač Spring Boota za podršku poslužitelju resursa. Ovaj početnik prema zadanim postavkama uključuje Spring Security, pa ga ne trebamo izričito dodavati:

 org.springframework.boot spring-boot-starter-web 2.2.6.RELEASE org.springframework.boot spring-boot-starter-oauth2-resource-server 2.2.6.RELEASE org.apache.commons commons-lang3 3.9 

Osim toga, dodali smo i web podršku.

U naše demonstracijske svrhe generirat ćemo resurse nasumce, umjesto da ih preuzmemo iz baze podataka, uz pomoć Apacheove pomoći commons-lang3 knjižnica.

4.2. Model

Jednostavno, koristit ćemo Foo, POJO, kao naš zaštićeni resurs:

javna klasa Foo {private long id; privatni naziv niza; // konstruktor, getteri i postavljači} 

4.3. API

Ovdje je naš kontroler odmora Foo dostupno za manipulaciju:

@RestController @RequestMapping (value = "/ foos") javna klasa FooController {@GetMapping (value = "/ {id}") public Foo findOne (@PathVariable Long id) {return new Foo (Long.parseLong (randomNumeric (2)) ), randomAlphabetic (4)); } @GetMapping javni popis findAll () {Lista fooList = novi ArrayList (); fooList.add (novi Foo (Long.parseLong (randomNumeric (2)), randomAlphabetic (4))); fooList.add (novi Foo (Long.parseLong (randomNumeric (2)), randomAlphabetic (4))); fooList.add (novi Foo (Long.parseLong (randomNumeric (2)), randomAlphabetic (4))); return fooList; } @ResponseStatus (HttpStatus.CREATED) @PostMapping javno prazno stvaranje (@RequestBody Foo newFoo) {logger.info ("Foo stvoren"); }}

Kao što je vidljivo, imamo odredbu da DOBIJEMO sve Foos, GET a Foo po id i POST a Foo.

4.4. Konfiguracija sigurnosti

U ovoj klasi konfiguracije definiramo razine pristupa za naš resurs:

@Configuration javna klasa JWTSecurityConfig proširuje WebSecurityConfigurerAdapter {@Override zaštićena void konfiguracija (HttpSecurity http) baca izuzetak {http .authorizeRequests (authz -> authz .antMatchers (HttpMMet.GET, "/foos/**"). .antMatchers (HttpMethod.POST, "/foos").hasAuthority("SCOPE_write") .anyRequest (). authenticated ()) .oauth2ResourceServer (oauth2 -> oauth2.jwt ()); }} 

Svatko s pristupnim tokenom koji ima čitati opseg može dobiti Foos. Da biste POSTAVILI novu Foo, njihov token trebao bi imati pisati opseg.

Dodatno, dodali smo poziv za jwt () koristiti oauth2ResourceServer () DSL za označavanje vrste tokena koje ovdje podržava naš poslužitelj.

4.5. primjena.iml

U svojstvima aplikacije, pored uobičajenog broja priključka i staze konteksta, moramo definirati put do URI-ja izdavača našeg autorizacijskog poslužitelja kako bi poslužitelj resursa mogao otkriti konfiguraciju svog davatelja usluga:

poslužitelj: port: 8081 servlet: context-path: / resource-server-jwt spring: security: oauth2: resourceserver: jwt: issuer-uri: // localhost: 8083 / auth / realms / baeldung

Poslužitelj resursa koristi ove informacije za provjeru JWT tokena koji dolaze iz klijentske aplikacije, prema koraku 9 našeg dijagrama sekvence.

Da bi ova provjera valjanosti koristila izdavatelj-uri svojstvo, autorizacijski poslužitelj mora biti pokrenut i pokrenut. Inače, poslužitelj resursa ne bi se pokrenuo.

Ako ga trebamo pokrenuti samostalno, tada možemo isporučiti jwk-set-uri svojstvo umjesto da upućuje na krajnju točku poslužitelja za autorizaciju koja izlaže javne ključeve:

jwk-set-uri: // localhost: 8083 / auth / realms / baeldung / protocol / openid-connect / certs

I to je sve što nam je potrebno da naš poslužitelj provjeri valjanost JWT tokena.

4.6. Testiranje

Za testiranje ćemo postaviti JUnit. Da bismo izvršili ovaj test, potreban nam je poslužitelj za autorizaciju kao i poslužitelj resursa koji je pokrenut i pokrenut.

Provjerimo da možemo doći Foos od resurs-poslužitelj-jwt s a čitati Scoped token u našem testu:

@Test javna praznina givenUserWithReadScope_whenGetFooResource_thenSuccess () {String accessToken = получитьAccessToken ("read"); Odgovor odgovora = RestAssured.given () .header (HttpHeaders.AUTHORIZATION, "Bearer" + accessToken) .get ("// localhost: 8081 / resource-server-jwt / foos"); assertThat (response.as (List.class)). hasSizeGreaterThan (0); }

U gornjem kodu, na retku # 3 dobivamo pristupni token s čitati opseg od autorizacijskog poslužitelja, pokrivajući korake od 1 do 7 našeg dijagrama redoslijeda.

Korak 8 izvodi Budite uvjereni‘S dobiti() poziv. Korak 9 izvodi poslužitelj resursa s konfiguracijama koje smo vidjeli i transparentan je nama kao korisnicima.

5. Poslužitelj resursa - Korištenje neprozirnih žetona

Dalje, pogledajmo iste komponente za naš poslužitelj resursa koji rukuju neprozirnim tokenima.

5.1. Ovisnosti Mavena

Da bismo podržali neprozirne tokene, dodatno će nam trebati oauth2-oidc-sdk ovisnost:

 com.nimbusds oauth2-oidc-sdk 8.19 vrijeme izvođenja 

5.2. Model i kontroler

Za ovaj ćemo dodati a Bar resurs:

javna klasa Bar {private long id; privatni naziv niza; // konstruktor, getteri i postavljači} 

Imat ćemo i BarController s krajnjim točkama sličnim našim FooController prije, za jelo Bars.

5.3. primjena.iml

U primjena.iml ovdje ćemo morati dodati introspekcija-uri što odgovara krajnjoj točki introspekcije našeg poslužitelja za autorizaciju. Kao što je već spomenuto, evo kako se neprozirni token provjerava:

poslužitelj: port: 8082 servlet: context-path: / resource-server-opaque spring: security: oauth2: resourceserver: opaque: introspection-uri: // localhost: 8083 / auth / realms / baeldung / protocol / openid-connect / token / introspect introspection-client-id: barClient introspection-client-secret: barClientSecret

5.4. Konfiguracija sigurnosti

Održavajući razine pristupa slične onima na Foo za Bar resurs, ova klasa konfiguracije također upućuje poziv opaqueToken () koristiti oauth2ResourceServer () DSL označava upotrebu neprozirnog tipa žetona:

@Configuration javna klasa OpaqueSecurityConfig proširuje WebSecurityConfigurerAdapter {@Value ("$ {spring.security.oauth2.resourceserver.opaque.introspection-uri}") String introspectionUri; @Value ("$ {spring.security.oauth2.resourceserver.opaque.introspection-client-id}") String clientId; @Value ("$ {spring.security.oauth2.resourceserver.opaque.introspection-client-secret}") String clientSecret; @Override zaštićena void konfiguracija (HttpSecurity http) baca iznimku {http .authorizeRequests (authz -> authz .antMatchers (HttpMethod.GET, "/bars/**").hasAuthority("SCOPE_read") .antMatchers (HttpMethod.POST, " /bars").hasAuthority("SCOPE_write ") .anyRequest (). authenticated ()) .oauth2ResourceServer (oauth2 -> oauth2 .opaqueToken (token -> token.introspectionUri (this.introspectionUri) .introspeentialsClient. clientSecret))); }} 

Ovdje također specificiramo vjerodajnice klijenta koje odgovaraju klijentu autorizacijskog poslužitelja koji ćemo koristiti. To smo definirali ranije u našem primjena.iml.

5.5. Testiranje

Postavit ćemo JUnit za naš neprozirni poslužitelj resursa zasnovan na tokenima, slično kao što smo to učinili za JWT.

U ovom slučaju, provjerimo je li a pisati token za pristup s opsegom može POST a Bar do resurs-poslužitelj-neproziran:

@Test javna praznina givenUserWithWriteScope_whenPostNewBarResource_thenCreated () {String accessToken = получитьAccessToken ("čitanje pisanja"); Traka newBar = nova traka (Long.parseLong (randomNumeric (2)), randomAlphabetic (4)); Odgovor odgovora = RestAssured.given () .contentType (ContentType.JSON) .header (HttpHeaders.AUTHORIZATION, "Bearer" + accessToken) .body (newBar) .log () .all () .post ("// localhost: 8082 / resource-server-opaque / bars "); assertThat (response.getStatusCode ()). isEqualTo (HttpStatus.CREATED.value ()); }

Ako dobijemo status CREATED natrag, to znači da je poslužitelj resursa uspješno potvrdio neprozirni token i stvorio Bar za nas.

6. Zaključak

U ovom uputstvu vidjeli smo kako konfigurirati aplikaciju poslužitelja resursa utemeljenu na Spring Security-u za provjeru valjanosti JWT-a kao i neprozirnih tokena.

Kao što smo vidjeli, uz minimalno postavljanje, Spring je omogućio neprimjetno validaciju tokena kod izdavača i pošaljite resurse stranci koja podnosi zahtjev - u našem slučaju JUnit test.

Kao i uvijek, izvorni kod dostupan je na GitHub-u.


$config[zx-auto] not found$config[zx-overlay] not found