Tavaszi REST API + OAuth2 + szögletes
1. Áttekintés
Ebben az oktatóanyagban egy REST API-t biztosítunk az OAuth2-vel, és egy egyszerű Angular kliensből használjuk fel.
A kiépítendő alkalmazás három különálló modulból áll:
- Authorization Server
- Erőforrás-kiszolgáló
- Felhasználói felület engedélyezési kódja: az engedélyezési kódfolyamatot használó kezelőfelület
Az OAuth-csomagot a Spring Security 5-ben fogjuk használni. Ha használni szeretné a Spring Security OAuth örökölt veremét, olvassa el ezt a korábbi cikket: Spring REST API + OAuth2 + Angular (a Spring Security OAuth örökség verem használata).
Ugorjunk be rögtön.
2. Az OAuth2 hitelesítési kiszolgáló (AS)
Egyszerűen fogalmazva, a Authorization Server olyan alkalmazás, amely jogkivonatokat ad ki az engedélyezéshez.
Korábban a Spring Security OAuth verem lehetőséget kínált egy Authorization Server telepítésére tavaszi alkalmazásként. De a projekt elavult, főleg azért, mert az OAuth egy nyílt szabvány, sok olyan jól bevált szolgáltatóval, mint például az Okta, a Keycloak és a ForgeRock, hogy csak néhányat említsünk.
Ezek közül a Keycloak-ot fogjuk használni. Ez egy nyílt forráskódú Identity and Access Management szerver, amelyet a Red Hat felügyel, Java-ban fejlesztett a JBoss. Nem csak az OAuth2, hanem más szabványos protokollokat is támogat, mint például az OpenID Connect és a SAML.
Ehhez az oktatóanyaghoz egy beágyazott Keycloak szervert hozunk létre egy Spring Boot alkalmazásban.
3. Az erőforrás-kiszolgáló (RS)
Most beszéljük meg az Erőforrás-kiszolgálót; ez lényegében a REST API, amelyet végül el akarunk használni.
3.1. Maven konfiguráció
Az erőforrás-kiszolgálónk nagyjából megegyezik az előző Authorization Server-kiszolgálóval, a Keycloak részt és a másikat használja egy további spring-boot-starter-oauth2-erőforrás-kiszolgáló függőség:
org.springframework.boot spring-boot-starter-oauth2-resource-server
3.2. Biztonsági konfiguráció
Mivel a Spring Boot-ot használjuk, megadhatjuk a minimálisan szükséges konfigurációt a Boot tulajdonságokkal.
Megcsináljuk ezt egy alkalmazás.yml fájl:
szerver: port: 8081 servlet: context-path: / erőforrás-kiszolgáló rugó: security: oauth2: resourceserver: jwt: kibocsátó-uri: // localhost: 8083 / auth / realms / baeldung jwk-set-uri: // localhost: 8083 / auth / realms / baeldung / protocol / openid-connect / certs
Itt megadtuk, hogy JWT tokent fogunk használni az engedélyezéshez.
A jwk-set-uri tulajdonság a nyilvános kulcsot tartalmazó URI-ra mutat, hogy az erőforrás-kiszolgálónk ellenőrizhesse a tokenek integritását.
A kibocsátó-uri tulajdonság egy további biztonsági intézkedést jelent a tokenek kibocsátójának hitelesítésére (amely az Authorization Server). Ennek a tulajdonságnak a hozzáadása azonban azt is előírja, hogy az Authorization Server-nek futtatnia kell, mielőtt elindíthatnánk az Resource Server alkalmazást.
Ezután állítsuk be a az API biztonsági konfigurációja a végpontok biztonságához:
A @Configuration public class SecurityConfig kiterjeszti a WebSecurityConfigurerAdapter {@Orride védett érvénytelen konfigurációt (HttpSecurity http) a {http.cors () .and () .authorizeRequests () .antMatchers (HttpMethod.GET, "/ user / info", "/ api / foos / ** ") .hasAuthority (" SCOPE_read ") .antMatchers (HttpMethod.POST," / api / foos ") .hasAuthority (" SCOPE_write ") .anyRequest () .authenticated () .and () .oauth2ResourceServer ). jwt (); }}
Mint láthatjuk, a GET módszereinknél csak olyan kérelmeket engedélyezünk, amelyeknek van olvas hatálya. A POST módszerhez a kérelmezőnek rendelkeznie kell a ír hatóság mellett olvas. Bármely más végpont esetében a kérést csak bármely felhasználóval hitelesíteni kell.
Is, a oauth2ResourceServer () metódus megadja, hogy ez egy erőforrás-kiszolgáló, a jwt () -formázott tokenek.
Egy másik szempont, amelyet itt meg kell jegyezni, a módszer használata cors () hogy engedélyezzék az Access-Control fejléceket a kéréseken. Ez különösen fontos, mivel Angular ügyféllel van dolgunk, és kéréseink egy másik eredet URL-ből származnak.
3.4. A modell és a tár
Ezután definiáljuk a javax.persistence.Entity modellünkhöz, Foo:
@Entity public class Foo {@Id @GeneratedValue (strategy = GenerationType.IDENTITY) private Long id; privát karakterlánc neve; // kivitelező, mérőeszközök és beállítók}
Akkor szükségünk van egy Foos. A Spring-et fogjuk használni PagingAndSortingRepository:
nyilvános felület Az IFooRepository kiterjeszti a PagingAndSortingRepository {}
3.4. A szolgáltatás és a megvalósítás
Ezt követően meghatározunk és megvalósítunk egy egyszerű szolgáltatást az API-nkhoz:
nyilvános felület IFooService {Opcionális findById (hosszú id); Foo mentés (Foo foo); Iterálható findAll (); } @Service public class A FooServiceImpl megvalósítja az IFooService {private IFooRepository fooRepository; public FooServiceImpl (IFooRepository fooRepository) {this.fooRepository = fooRepository; } @Override public Opcionális findById (hosszú id) {return fooRepository.findById (id); } @Orride public Foo save (Foo foo) {return fooRepository.save (foo); } @Orride public Iterable findAll () {return fooRepository.findAll (); }}
3.5. Egy minta vezérlő
Most valósítsunk meg egy egyszerű vezérlőt, amely leleplezi a sajátunkat Foo erőforrás DTO-n keresztül:
@RestController @RequestMapping (value = "/ api / foos") nyilvános osztály FooController {private IFooService fooService; public FooController (IFooService fooService) {this.fooService = fooService; } @CrossOrigin (origins = "// localhost: 8089") @GetMapping (value = "/ {id}") public FooDto findOne (@PathVariable Long id) {Foo entitás = fooService.findById (id) .orElseThrow (() -> új ResponseStatusException (HttpStatus.NOT_FOUND)); return convertToDto (entitás); } @GetMapping nyilvános gyűjtemény findAll () {Iterable foos = this.fooService.findAll (); FooDtos = new ArrayList () lista; foos.forEach (p -> fooDtos.add (convertToDto (p))); visszatér fooDtos; } védett FooDto convertToDto (Foo entitás) {FooDto dto = új FooDto (entitás.getId (), entitás.getName ()); return dto; }}
Figyelje meg a @CrossOrigin felett; ez a vezérlőszintű konfiguráció, amelyet engedélyeznünk kell a CORS-nak az Angular App-ból a megadott URL-en.
Itt van a miénk FooDto:
nyilvános osztály FooDto {private long id; privát karakterlánc neve; }
4. Kezelőfelület - Beállítás
Most egy egyszerű front-end Angular megvalósítást fogunk megvizsgálni az ügyfél számára, amely hozzáférni fog a REST API-hoz.
Először az Angular CLI-t használjuk az előtér-modulok előállításához és kezeléséhez.
Először telepítjük a csomópontot és az npm-et, mivel az Angular CLI egy npm eszköz.
Akkor használnunk kell a frontend-maven-plugin hogy megépítsük Angular projektünket Maven segítségével:
com.github.eirslett frontend-maven-plugin 1.3 v6.10.2 3.10.10 src / main / resources install node és npm install-node-and-npm npm install npm npm run build npm run build
És végül, hozzon létre egy új modult az Angular CLI használatával:
új oauthApp
A következő részben az Angular alkalmazás logikáját tárgyaljuk.
5. Engedélyezési kód folyamata szöggel
Itt fogjuk használni az OAuth2 engedélyezési kód folyamatát.
Felhasználási esetünk: Az ügyfélalkalmazás kódot kér az Authorization Server-től, és megjelenik egy bejelentkezési oldal. Miután a felhasználó megadta érvényes hitelesítő adatait és elküldte, az Authorization Server megadja nekünk a kódot. Ezután a kezelőfelület egy hozzáférési token megszerzésére használja.
5.1. Otthoni alkatrész
Kezdjük a fő összetevőnkkel, a HomeComponent, ahol az összes művelet megkezdődik:
@Component ({selector: 'home-header', szolgáltatók: [AppService], sablon: `Bejelentkezés Üdvözöljük !! Kijelentkezés`}) export osztály HomeComponent {public isLoggedIn = false; konstruktor (privát _szolgáltatás: AppService) {} ngOnInit () {this.isLoggedIn = this._service.checkCredentials (); legyen i = window.location.href.indexOf ('kód'); if (! this.isLoggedIn && i! = -1) {this._service.retrieveToken (window.location.href.substring (i + 5)); }} login () {window.location.href = '// localhost: 8083 / auth / realms / baeldung / protokoll / openid-connect / auth? response_type = kód & hatókör = openid% 20write% 20read & client_id = '+ this._service.clientId +' & redirect_uri = '+ this._service.redirectUri; } kijelentkezés () {this._service.logout (); }}
Kezdetben, ha a felhasználó nincs bejelentkezve, csak a bejelentkezési gomb jelenik meg. Erre a gombra kattintva a felhasználó eljut a hozzáférési rendszer engedélyezési URL-jéhez, ahol beírja a felhasználónevet és a jelszót. Sikeres bejelentkezés után a felhasználót visszautaljuk a hitelesítési kóddal, majd a kód segítségével lekérjük a hozzáférési tokent.
5.2. App Service
Most nézzük meg AppService - található app.service.ts - amely tartalmazza a szerver interakciók logikáját:
- retrieveToken (): hozzáférési jogkivonat beszerzése engedélyezési kód használatával
- saveToken (): hozzáférési tokenünk mentése egy cookie-ba az ng2-cookies könyvtár segítségével
- getResource (): egy Foo objektum megszerzése a szerverről az azonosítója használatával
- checkCredentials (): annak ellenőrzése, hogy a felhasználó be van-e jelentkezve, vagy sem
- Kijelentkezés(): a hozzáférési token cookie törléséhez és a felhasználó kijelentkezéséhez
export osztály Foo {konstruktor (nyilvános azonosító: szám, nyilvános név: karakterlánc) {}} @Injectable () export osztály AppService {public clientId = 'newClient'; public redirectUri = '// localhost: 8089 /'; konstruktor (private _http: HttpClient) {} retrieveToken (code) {let params = new URLSearchParams (); params.append ('grant_type', 'authorization_code'); params.append ('client_id', this.clientId); params.append ('client_secret', 'newClientSecret'); params.append ('redirect_uri', this.redirectUri); params.append ('kód', kód); let fejlécek = 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: header}) .subscribe (data => this.saveToken ( adatok), err => alert ('Érvénytelen hitelesítő adatok')); } saveToken (token) {var expireDate = new Date (). getTime () + (1000 * token.expires_in); Cookie.set ("access_token", token.access_token, expireDate); console.log ('Hozzáférési token'); window.location.href = '// localhost: 8089'; } getResource (resourceUrl): Megfigyelhető {var headers = new HttpHeaders ({'Content-type': 'application / x-www-form-urlencoded; charset = utf-8', 'Authorization': 'Bearer' + Cookie.get ('access_token')}); adja vissza ezt :_http.get (resourceUrl, {fejlécek: fejlécek}) .catch ((hiba: bármelyik) => Megfigyelhető.throw (hiba.json (). hiba || 'Szerverhiba')); } checkCredentials () {return Cookie.check ('access_token'); } kijelentkezés () {Cookie.delete ('access_token'); window.location.reload (); }}
Ban,-ben retrieveToken módszerrel az ügyfél hitelesítő adatainkat és az Alap hitelesítést használjuk a POST hoz / openid-connect / token végpont a hozzáférési jogkivonat megszerzéséhez. A paramétereket URL-kódolású formátumban küldjük. Miután megszereztük a hozzáférési tokent, sütiben tároljuk.
A sütitárolás itt különösen fontos, mert a sütit csak tárolási célokra használjuk, és nem közvetlenül a hitelesítési folyamatot hajtjuk végre. Ez segít megvédeni a webhelyek közötti kérelmek hamisítását (CSRF) támadásokat és sebezhetőségeket.
5.3. Foo komponens
Végül a mi FooComponent Foo adataink megjelenítéséhez:
@Component ({selector: 'foo-details', szolgáltatók: [AppService], sablon: `ID {{foo.id}} Név {{foo.name}} New Foo`}) exportálási osztály FooComponent {public foo = new Foo (1, 'minta foo'); private foosUrl = '// localhost: 8081 / erőforrás-szerver / api / foos /'; konstruktor (privát _szolgáltatás: AppService) {} getFoo () {this._service.getResource (this.foosUrl + this.foo.id) .subscribe (data => this.foo = data, error => this.foo.name = 'Hiba'); }}
5.5. App Component
Egyszerű AppComponent hogy gyökérkomponensként működjön:
@Component ({selector: 'app-root', template: `Spring Security Oauth - Authorization Code`}) exportálási osztály AppComponent {}
És a AppModule ahol az összes alkatrészünket, szolgáltatásunkat és útvonalunkat becsomagoljuk:
@NgModule ({deklarációk: [AppComponent, HomeComponent, FooComponent], import: [BrowserModule, HttpClientModule, RouterModule.forRoot ([{path: '', component: HomeComponent, pathMatch: 'full'}], {onSameUroadNavigation })], szolgáltatók: [], bootstrap: [AppComponent]}) exportálási osztály AppModule {}
7. Futtassa a kezelőfelületet
1. A front-end modulok futtatásához először fel kell építenünk az alkalmazást:
mvn tiszta telepítés
2. Ezután el kell navigálnunk az Angular alkalmazás könyvtárunkba:
cd src / main / resources
3. Végül elindítjuk az alkalmazást:
npm kezdés
A szerver alapértelmezés szerint a 4200-as porton indul; bármely modul portjának módosításához módosítsa:
"start": "ng tálalás"
ban ben csomag.json; Például a 8089-es porton való futtatásához adja hozzá:
"start": "ng serve - port 8089"
8. Következtetés
Ebben a cikkben megtudtuk, hogyan engedélyezhetjük alkalmazásunkat az OAuth2 használatával.
Az oktatóanyag teljes megvalósítása megtalálható a GitHub projektben.