OAuth 2.0-bronserver met Spring Security 5

1. Overzicht

In deze tutorial leren we hoe u een OAuth 2.0-bronserver instelt met Spring Security 5.

We zullen dit doen met zowel JWT als ondoorzichtige tokens, de twee soorten drager-tokens die worden ondersteund door Spring Security.

Voordat we verder gaan met de implementatie en codevoorbeelden, zullen we wat achtergrondinformatie vastleggen.

2. Een beetje achtergrond

2.1. Wat zijn JWT's en ondoorzichtige tokens?

JWT of JSON Web Token is een manier om gevoelige informatie veilig over te dragen in het algemeen aanvaarde JSON-formaat. De opgenomen informatie kan over de gebruiker gaan, of over het token zelf, zoals de vervaldatum en uitgever.

Aan de andere kant is een ondoorzichtig token, zoals de naam suggereert, ondoorzichtig in termen van de informatie die het bevat. Het token is slechts een identificatie die verwijst naar de informatie die is opgeslagen op de autorisatieserver - het wordt gevalideerd via introspectie aan het einde van de server.

2.2. Wat is een bronserver?

In de context van OAuth 2.0, een resource server is een applicatie die bronnen beschermt via OAuth-tokens. Deze tokens worden uitgegeven door een autorisatieserver, meestal aan een clienttoepassing. De taak van de bronserver is om het token te valideren voordat een bron aan de client wordt aangeboden.

De geldigheid van een token wordt bepaald door verschillende dingen:

  • Is dit token afkomstig van de geconfigureerde autorisatieserver?
  • Is het niet verlopen?
  • Is deze bronserver het beoogde publiek?
  • Heeft het token de vereiste autoriteit om toegang te krijgen tot de gevraagde bron?

Laten we, om dit te visualiseren, een sequentiediagram bekijken voor de autorisatiecodestroom en alle actoren in actie zien:

Zoals we kunnen zien in stap 8, gaat de clienttoepassing die de API van de bronserver aanroept om toegang te krijgen tot een beschermde bron, eerst naar de autorisatieserver om het token in het verzoek te valideren. Autorisatie: drager header, en reageert vervolgens op de client.

Stap 9 is waar we ons in deze tutorial op concentreren.

Oké, laten we nu in het codegedeelte springen. We zullen een autorisatieserver opzetten met behulp van Keycloak, een bronserver die JWT-tokens valideert, een andere bronserver die ondoorzichtige tokens valideert, en een paar JUnit-tests om client-apps te simuleren en reacties te verifiëren.

3. Autorisatieserver

Eerst gaan we een autorisatieserver opzetten, of het ding dat tokens uitgeeft.

Daarvoor gebruiken we Keycloak ingebed in een Spring Boot-applicatie. Keycloak is een open-source oplossing voor identiteits- en toegangsbeheer. Omdat we ons in deze tutorial concentreren op de bronserver, zullen we er niet dieper op ingaan.

Onze embedded Keycloak Server heeft twee gedefinieerde clients - fooClient en barClient - overeenkomend met onze twee resource servertoepassingen.

4. Bronserver - JWT's gebruiken

Onze bronserver heeft vier hoofdcomponenten:

  • Model - de bron die moet worden beschermd
  • API - een REST-controller om de bron vrij te geven
  • Beveiligingsconfiguratie - een klasse om toegangscontrole te definiëren voor de beschermde bron die de API blootlegt
  • application.yml - een configuratiebestand om eigenschappen te declareren, inclusief informatie over de autorisatieserver

Laten we ze een voor een bekijken voor onze bronserver die JWT-tokens verwerkt, nadat we een kijkje hebben genomen naar de afhankelijkheden.

4.1. Afhankelijkheden van Maven

We hebben voornamelijk de spring-boot-starter-oauth2-resource-server, Spring Boot's starter voor resource server-ondersteuning. Deze starter bevat standaard Spring Security, dus we hoeven het niet expliciet toe te voegen:

 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 

Afgezien daarvan hebben we ook webondersteuning toegevoegd.

Voor onze demonstratiedoeleinden zullen we bronnen willekeurig genereren in plaats van ze uit een database te halen, met wat hulp van Apache commons-lang3 bibliotheek.

4.2. Model

Om het simpel te houden, gebruiken we Foo, een POJO, als onze beschermde bron:

openbare klasse Foo {lange privé-id; private String naam; // constructeur, getters en setters} 

4.3. API

Hier is onze restcontroller, om te maken Foo beschikbaar voor manipulatie:

@RestController @RequestMapping (value = "/ foos") openbare klasse FooController {@GetMapping (value = "/ {id}") openbare Foo findOne (@PathVariable Lange id) {return nieuwe Foo (Long.parseLong (randomNumeric (2) ), willekeurig Alfabetisch (4)); } @GetMapping openbare lijst findAll () {Lijst fooList = nieuwe ArrayList (); fooList.add (nieuwe Foo (Long.parseLong (randomNumeric (2)), randomAlphabetic (4))); fooList.add (nieuwe Foo (Long.parseLong (randomNumeric (2)), randomAlphabetic (4))); fooList.add (nieuwe Foo (Long.parseLong (randomNumeric (2)), randomAlphabetic (4))); terugkeer fooList; } @ResponseStatus (HttpStatus.CREATED) @PostMapping public void create (@RequestBody Foo newFoo) {logger.info ("Foo gemaakt"); }}

Zoals duidelijk is, hebben we de mogelijkheid om alles te KRIJGEN Foos, KRIJG een Foo door id, en POST een Foo.

4.4. Beveiligingsconfiguratie

In deze configuratieklasse definiëren we toegangsniveaus voor onze bron:

@Configuration public class JWTSecurityConfig breidt WebSecurityConfigurerAdapter uit {@Override protected void configure (HttpSecurity http) genereert uitzondering {http .authorizeRequests (authz -> authz .antMatchers (HttpMethod.GET, "/foos/**").has .antMatchers (HttpMethod.POST, "/foos").hasAuthority("SCOPE_write") .anyRequest (). geauthenticeerd ()) .oauth2ResourceServer (oauth2 -> oauth2.jwt ()); }} 

Iedereen met een toegangstoken met de extensie lezen scope kan krijgen Foos. Om een ​​nieuw Foo, moet hun token een schrijven reikwijdte.

Bovendien, we hebben een oproep toegevoegd aan jwt () de ... gebruiken oauth2ResourceServer () DSL om hier het type tokens aan te geven dat door onze server wordt ondersteund.

4.5. application.yml

In de applicatie-eigenschappen, naast het gebruikelijke poortnummer en contextpad, we moeten het pad naar de uitgever-URI van onze autorisatieserver definiëren, zodat de bronserver zijn providerconfiguratie kan ontdekken:

server: poort: 8081 servlet: contextpad: / resource-server-jwt spring: security: oauth2: resourceserver: jwt: issuer-uri: // localhost: 8083 / auth / realms / baeldung

De bronserver gebruikt deze informatie om de JWT-tokens te valideren die binnenkomen vanuit de clienttoepassing, zoals beschreven in stap 9 van ons sequentiediagram.

Om deze validatie te laten werken met behulp van de uitgever-uri eigenschap, moet de autorisatieserver actief zijn. Anders zou de bronserver niet starten.

Als we het zelfstandig moeten starten, kunnen we het jwk-set-uri eigenschap om te verwijzen naar het eindpunt van de autorisatieserver dat openbare sleutels vrijgeeft:

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

En dat is alles wat we nodig hebben om onze server JWT-tokens te laten valideren.

4.6. Testen

Om te testen, zullen we een JUnit opzetten. Om deze test uit te voeren, hebben we zowel de autorisatieserver als de bronserver nodig.

Laten we verifiëren dat we kunnen krijgen Foos van resource-server-jwt met een lezen scoped token in onze test:

@Test openbare ongeldige gegevenUserWithReadScope_whenGetFooResource_thenSuccess () {String accessToken = getAccessToken ("lezen"); Antwoordantwoord = RestAssured.given () .header (HttpHeaders.AUTHORIZATION, "Bearer" + accessToken) .get ("// localhost: 8081 / resource-server-jwt / foos"); assertThat (response.as (List.class)). hasSizeGreaterThan (0); }

In de bovenstaande code krijgen we op regel # 3 een toegangstoken met lezen bereik van de autorisatieserver, die de stappen 1 tot en met 7 van ons sequentiediagram omvat.

Stap 8 wordt uitgevoerd door Wees gerustgesteld‘S krijgen() bellen. Stap 9 wordt uitgevoerd door de bronserver met de configuraties die we hebben gezien en is transparant voor ons als gebruikers.

5. Bronserver - Ondoorzichtige tokens gebruiken

Laten we vervolgens dezelfde componenten bekijken voor onze bronserver die ondoorzichtige tokens verwerkt.

5.1. Afhankelijkheden van Maven

Om ondoorzichtige tokens te ondersteunen, hebben we bovendien de oauth2-oidc-sdk afhankelijkheid:

 com.nimbusds oauth2-oidc-sdk 8.19 runtime 

5.2. Model en controller

Voor deze zullen we een Bar bron:

openbare klasse Bar {privé lange id; private String naam; // constructeur, getters en setters} 

We hebben ook een BarController met eindpunten vergelijkbaar met onze FooController ervoor, om uit te delen Bars.

5.3. application.yml

In de application.yml hier moeten we een introspectie-uri overeenkomend met het introspectie-eindpunt van onze autorisatieserver. Zoals eerder vermeld, is dit hoe een ondoorzichtig token wordt gevalideerd:

server: poort: 8082 servlet: contextpad: / resource-server-ondoorzichtig 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. Beveiligingsconfiguratie

Toegangsniveaus vergelijkbaar houden met die van Foo voor de Bar bron ook, deze configuratieklasse doet ook een oproep naar opaqueToken () de ... gebruiken oauth2ResourceServer () DSL om het gebruik van het ondoorzichtige token-type aan te geven:

@Configuration openbare klasse OpaqueSecurityConfig breidt WebSecurityConfigurerAdapter {@Value ("$ {spring.security.oauth2.resourceserver.opaque.introspection-uri}") String introspectionUri uit; @Value ("$ {spring.security.oauth2.resourceserver.opaque.introspection-client-id}") String clientId; @Value ("$ {spring.security.oauth2.resourceserver.opaque.introspection-client-secret}") String clientSecret; @Override protected void configure (HttpSecurity http) genereert uitzondering {http .authorizeRequests (authz -> authz .antMatchers (HttpMethod.GET, "/bars/**").hasAuthority("SCOPE_read") .antMatchers (HttpMethod.POST, " /bars").hasAuthority("SCOPE_write ") .anyRequest (). geverifieerd ()) .oauth2ResourceServer (oauth2 -> oauth2 .opaqueToken (token -> token.introspectionUri (this.introspectionUri) .introspectionClient. clientSecret))); }} 

Hier specificeren we ook de clientreferenties die overeenkomen met de client van de autorisatieserver die we zullen gebruiken. We hebben deze eerder in ons gedefinieerd application.yml.

5.5. Testen

We zullen een JUnit opzetten voor onze ondoorzichtige token-gebaseerde bronserver, vergelijkbaar met hoe we het deden voor de JWT-server.

Laten we in dit geval eens kijken of a schrijven scoped access token kan een Bar naar resource-server-ondoorzichtig:

@Test openbare leegte gegevenUserWithWriteScope_whenPostNewBarResource_thenCreated () {String accessToken = getAccessToken ("lezen schrijven"); Bar newBar = nieuwe Bar (Long.parseLong (randomNumeric (2)), randomAlphabetic (4)); Antwoordantwoord = 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 ()); }

Als we de status CREATED terugkrijgen, betekent dit dat de bronserver met succes het ondoorzichtige token heeft gevalideerd en de Bar voor ons.

6. Conclusie

In deze zelfstudie hebben we gezien hoe u een op Spring Security gebaseerde resource servertoepassing configureert voor het valideren van zowel JWT als ondoorzichtige tokens.

Zoals we zagen, Met minimale setup maakte Spring het mogelijk om de tokens naadloos te valideren bij een uitgever en middelen naar de vragende partij sturen - in ons geval een JUnit-test.

Zoals altijd is de broncode beschikbaar op GitHub.


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