Jarné REST API + OAuth2 + uhlové

1. Prehľad

V tomto návode zabezpečíme REST API s OAuth2 a spotrebujeme ho od jednoduchého uhlového klienta.

Aplikácia, ktorú vytvoríme, bude pozostávať z troch samostatných modulov:

  • Autorizačný server
  • Zdrojový server
  • Autorizačný kód používateľského rozhrania: front-end aplikácia využívajúca tok autorizačného kódu

Zásobník OAuth použijeme v Spring Security 5. Ak chcete použiť starší zásobník Spring Security OAuth, pozrite si tento predchádzajúci článok: Spring REST API + OAuth2 + Angular (Používanie balíka Spring Security OAuth Legacy Stack).

Poďme priamo dovnútra.

2. Autorizačný server OAuth2 (AS)

Jednoducho povedané, autorizačný server je aplikácia, ktorá vydáva tokeny na autorizáciu.

Zásobník Spring Security OAuth predtým ponúkol možnosť nastavenia autorizačného servera ako jarnej aplikácie. Projekt je však zastaraný, hlavne preto, že OAuth je otvorený štandard s mnohými zavedenými poskytovateľmi, ako sú Okta, Keycloak a ForgeRock.

Z nich budeme používať Keycloak. Je to server správy identít a prístupu s otvoreným zdrojovým kódom, ktorý spravuje spoločnosť Red Hat vyvinutá v Jave spoločnosťou JBoss. Podporuje nielen OAuth2, ale aj ďalšie štandardné protokoly ako OpenID Connect a SAML.

Pre tento návod nastavíme vložený server Keycloak v aplikácii Spring Boot.

3. Zdrojový server (RS)

Teraz poďme diskutovať o zdrojovom serveri; toto je v podstate REST API, ktoré v konečnom dôsledku chceme mať možnosť konzumovať.

3.1. Konfigurácia Maven

Pom komponenta nášho Resource Servera je takmer rovnaká ako predchádzajúca pompom predchádzajúceho autorizačného servera, bez ohľadu na časť Keycloak dodatočný spring-boot-starter-oauth2-resource-server závislosť:

 org.springframework.boot server spring-boot-starter-oauth2-resource-server 

3.2. Konfigurácia zabezpečenia

Pretože používame Spring Boot, môžeme definovať minimálnu požadovanú konfiguráciu pomocou vlastností Boot.

Urobíme to v aplikácia.yml spis:

server: port: 8081 servlet: kontextová cesta: / zdroj-server jar: zabezpečenie: oauth2: resourceserver: jwt: issuer-uri: // localhost: 8083 / auth / realms / baeldung jwk-set-uri: // localhost: 8083 / auth / realms / baeldung / protocol / openid-connect / certs

Tu sme určili, že na autorizáciu použijeme tokeny JWT.

The jwk-set-uri Vlastnosť ukazuje na URI obsahujúci verejný kľúč, aby náš zdrojový server mohol overiť integritu tokenov.

The vydavateľ-uri property predstavuje ďalšie bezpečnostné opatrenie na overenie vydavateľa tokenov (ktorým je autorizačný server). Pridaním tejto vlastnosti sa však tiež vyžaduje, aby bol pred spustením aplikácie Resource Server spustený autorizačný server.

Ďalej nastavíme a konfigurácia zabezpečenia pre API na zabezpečenie koncových bodov:

@Configuration verejná trieda SecurityConfig rozširuje WebSecurityConfigurerAdapter {@Override chránená neplatná konfigurácia (HttpSecurity http) vyvolá výnimku {http.cors (). A () .authorizeRequests () .antMatchers (HttpMethod.GET, "/ user / info", "/ api / foos / ** ") .hasAuthority (" SCOPE_read ") .antMatchers (HttpMethod.POST," / api / foos ") .hasAuthority (" SCOPE_write ") .anyRequest () .authenticated (). a () .oauth2ResourceServer ( ) .jwt (); }}

Ako vidíme, pre naše metódy GET povoľujeme iba žiadosti, ktoré majú čítať rozsah. Pre metódu POST musí mať žiadateľ a napíš orgán okrem čítať. Pre akýkoľvek iný koncový bod by však mala byť požiadavka autentifikovaná akýmkoľvek používateľom.

Tiež the oauth2ResourceServer () metóda určuje, že sa jedná o zdrojový server s jwt () -naformátované tokeny.

Ďalším bodom, ktorý je potrebné tu poznamenať, je použitie metódy kory () povoliť hlavičky kontroly prístupu na požiadavkách. To je obzvlášť dôležité, pretože máme do činenia s klientom Angular a naše požiadavky budú pochádzať z inej pôvodnej adresy URL.

3.4. Model a úložisko

Ďalej definujeme a javax.persistence.Entity pre náš model, Foo:

@Entity public class Foo {@Id @GeneratedValue (strategy = GenerationType.IDENTITY) private Long id; súkromné ​​meno reťazca; // konštruktor, getri a nastavovatelia}

Potom potrebujeme úložisko Foos. Použijeme Spring PagingAndSortingRepository:

verejné rozhranie IFooRepository rozširuje PagingAndSortingRepository {} 

3.4. Služba a implementácia

Potom definujeme a implementujeme jednoduchú službu pre naše API:

verejné rozhranie IFooService {Voliteľné findById (Long id); Foo save (Foo foo); Iterovateľný findAll (); } @Service verejná trieda FooServiceImpl implementuje IFooService {private IFooRepository fooRepository; public FooServiceImpl (IFooRepository fooRepository) {this.fooRepository = fooRepository; } @Override public Voliteľné findById (Long id) {return fooRepository.findById (id); } @Override public Foo save (Foo foo) {return fooRepository.save (foo); } @Override public Iterable findAll () {return fooRepository.findAll (); }} 

3.5. Kontrolór vzorky

Teraz poďme implementovať jednoduchý radič, ktorý odhaľuje naše Foo zdroj prostredníctvom DTO:

@RestController @RequestMapping (value = "/ api / foos") verejná trieda FooController {súkromná IFooService fooService; public FooController (IFooService fooService) {this.fooService = fooService; } @CrossOrigin (origins = "// localhost: 8089") @GetMapping (value = "/ {id}") verejné FooDto findOne (@PathVariable Long id) {Foo entity = fooService.findById (id) .orElseThrow (() -> nová ResponseStatusException (HttpStatus.NOT_FOUND)); return convertToDto (entity); } @GetMapping public Collection findAll () {Iterable foos = this.fooService.findAll (); Zoznam fooDtos = nový ArrayList (); foos.forEach (p -> fooDtos.add (convertToDto (p))); návrat fooDtos; } chránené FooDto convertToDto (Foo entita) {FooDto dto = new FooDto (entity.getId (), entity.getName ()); návrat dto; }}

Všimnite si použitie @CrossOrigin vyššie; toto je konfigurácia na úrovni radiča, ktorú musíme povoliť, aby CORS z našej uhlovej aplikácie bežal na zadanej adrese URL.

Tu je náš FooDto:

public class FooDto {private long id; súkromné ​​meno reťazca; }

4. Klientske rozhranie - nastavenie

Teraz sa pozrieme na jednoduchú frontálnu implementáciu Angular pre klienta, ktorý bude mať prístup k nášmu REST API.

Najskôr použijeme Angular CLI na generovanie a správu našich front-end modulov.

Najskôr nainštalujeme node a npm, keďže Angular CLI je nástroj NPM.

Potom musíme použiť frontend-maven-plugin postaviť náš projekt Angular pomocou Mavenu:

   com.github.eirslett frontend-maven-plugin 1.3 v6.10.2 3.10.10 src / main / resources nainštalovať uzol a npm install-node-and-npm npm nainštalovať npm npm run build npm run build 

A nakoniec, vygenerujte nový modul pomocou Angular CLI:

ng nová oauthApp

V nasledujúcej časti si rozoberieme logiku aplikácie Angular.

5. Tok autorizačného kódu pomocou uhla

Použijeme tu tok autorizačného kódu OAuth2.

Náš prípad použitia: Klientska aplikácia vyžaduje kód z autorizačného servera a zobrazí sa mu prihlasovacia stránka. Len čo používateľ poskytne svoje platné poverenia a odošle, autorizačný server nám poskytne kód. Potom ho klient front-end použije na získanie prístupového tokenu.

5.1. Domáce komponenty

Začnime našou hlavnou súčasťou, ktorou je HomeComponent, kde sa začína všetka akcia:

@Component ({selector: 'home-header', providers: [AppService], template: `Login Welcome !! Logout

`}) exportovať triedu HomeComponent {public isLoggedIn = false; konštruktor (private _service: AppService) {} ngOnInit () {this.isLoggedIn = this._service.checkCredentials (); nech i = window.location.href.indexOf ('code'); if (! this.isLoggedIn && i! = -1) {this._service.retrieveToken (window.location.href.substring (i + 5)); }} login () {window.location.href = '// localhost: 8083 / auth / realms / baeldung / protocol / openid-connect / auth? response_type = code & scope = openid% 20write% 20read & client_id = '+ this._service.clientId +' & redirect_uri = '+ this._service.redirectUri; } odhlásenie () {this._service.logout (); }}

Na začiatku, keď používateľ nie je prihlásený, zobrazí sa iba prihlasovacie tlačidlo. Po kliknutí na toto tlačidlo sa používateľ dostane na autorizačnú adresu URL AS, kde zadá používateľské meno a heslo. Po úspešnom prihlásení je používateľ presmerovaný späť s autorizačným kódom a pomocou tohto kódu potom načítame prístupový token.

5.2. App Service

Teraz sa pozrime na AppService - umiestnený na app.service.ts - ktorý obsahuje logiku pre interakcie so serverom:

  • retrieveToken (): získanie prístupového tokenu pomocou autorizačného kódu
  • saveToken (): uložiť náš prístupový token do súboru cookie pomocou knižnice ng2-cookies
  • getResource (): získať objekt Foo zo servera pomocou jeho ID
  • checkCredentials (): skontrolovať, či je používateľ prihlásený alebo nie
  • odhlásiť sa(): odstránenie cookie prístupového tokenu a odhlásenie používateľa
exportná trieda Foo {konštruktor (verejné id: číslo, verejné meno: reťazec) {}} @Injectable () exportná trieda AppService {verejný klientId = 'nový klient'; public redirectUri = '// localhost: 8089 /'; konštruktor (private _http: HttpClient) {} retrieveToken (kód) {let params = new URLSearchParams (); params.append ('grant_type', 'autorizačný_kód'); params.append ('client_id', this.clientId); params.append ('client_secret', 'newClientSecret'); params.append ('redirect_uri', this.redirectUri); params.append ('code', code); let headers = new HttpHeaders ({'Content-type': 'application / x-www-form-urlencoded; charset = utf-8'}); this._http.post ('// localhost: 8083 / auth / realms / baeldung / protocol / openid-connect / token', params.toString (), {headers: headers}) .subscribe (data => this.saveToken ( data), err => alert ('Invalid Credentials')); } saveToken (token) {var expireDate = new Date (). getTime () + (1000 * token.expires_in); Cookie.set ("access_token", token.access_token, expireDate); console.log ('Získaný prístupový token'); window.location.href = '// localhost: 8089'; } getResource (resourceUrl): Pozorovateľné {var headers = new HttpHeaders ({'Content-type': 'application / x-www-form-urlencoded; charset = utf-8', 'Authorization': 'Bearer' + Cookie.get ('prístupový token')}); return this._http.get (resourceUrl, {headers: headers}) .catch ((error: any) => Observable.throw (error.json (). error || 'Chyba servera')); } checkCredentials () {return Cookie.check ('access_token'); } odhlásenie () {Cookie.delete ('access_token'); window.location.reload (); }}

V retrieveToken metódou, používame prihlasovacie údaje klienta a základné overenie na odoslanie a POST do / openid-connect / token koncový bod na získanie prístupového tokenu. Parametre sa odosielajú vo formáte kódovanom URL. Po získaní prístupového tokenu ukladáme to do cookie.

Uloženie súborov cookie je tu obzvlášť dôležité, pretože súbory cookie používame iba na účely ukladania a nie na účely priameho overovania. To pomáha chrániť sa pred útokmi a zraniteľnými miestami Cross-Site Request Forgery (CSRF).

5.3. Foo komponent

Nakoniec náš FooComponent pre zobrazenie našich Foo detailov:

@Component ({selector: 'foo-details', providers: [AppService], template: `ID {{foo.id}} Name {{foo.name}} New Foo`})) export class FooComponent {public foo = new Foo (1, „sample foo“); private foosUrl = '// localhost: 8081 / resource-server / api / foos /'; konštruktér (private _service: AppService) {} getFoo () {this._service.getResource (this.foosUrl + this.foo.id) .subscribe (data => this.foo = data, error => this.foo.name = 'Chyba'); }}

5.5. Komponent aplikácie

Naše jednoduché AppComponent pôsobiť ako koreňová zložka:

@Component ({selector: 'app-root', šablóna: `Spring Security Oauth - autorizačný kód`}) exportná trieda AppComponent {} 

A AppModule kde zabalíme všetky naše súčasti, služby a trasy:

@NgModule ({deklarácie: [AppComponent, HomeComponent, FooComponent], import: [BrowserModule, HttpClientModule, RouterModule.forRoot ([{cesta: '', komponent: HomeComponent, pathMatch: 'full'}]), {onSameUrlNavigation: 'znovu načítať' })], poskytovatelia: [], bootstrap: [AppComponent]}) trieda exportu AppModule {} 

7. Spustite klientske rozhranie

1. Ak chcete spustiť ktorýkoľvek z našich front-end modulov, musíme si najskôr vytvoriť aplikáciu:

mvn čistá inštalácia

2. Potom musíme prejsť do nášho adresára Angular app:

cd src / main / resources

3. Nakoniec spustíme našu aplikáciu:

npm štart

Server sa štandardne spustí na porte 4200; ak chcete zmeniť port ľubovoľného modulu, zmeňte:

"start": "ng serve"

v package.json; napríklad aby to fungovalo na porte 8089, pridajte:

"start": "ng serve --port 8089"

8. Záver

V tomto článku sme sa dozvedeli, ako autorizovať našu aplikáciu pomocou protokolu OAuth2.

Celú implementáciu tohto tutoriálu nájdete v projekte GitHub.


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