OAuth 2.0 ressursserver med vårsikkerhet 5

1. Oversikt

I denne opplæringen lærer vi hvordan du konfigurerer en OAuth 2.0-ressursserver ved hjelp av Spring Security 5.

Vi gjør dette ved hjelp av JWT så vel som ugjennomsiktige tokens, de to typer bærer-tokens som støttes av Spring Security.

Før vi hopper videre til implementerings- og kodeeksemplene, etablerer vi litt bakgrunn.

2. En liten bakgrunn

2.1. Hva er JWT-er og ugjennomsiktige tokens?

JWT, eller JSON Web Token, er en måte å overføre sensitiv informasjon sikkert i det allment aksepterte JSON-formatet. Den inneholder informasjonen kan være om brukeren, eller om selve tokenet, for eksempel utløpet og utstederen.

På den annen side er en ugjennomsiktig token, som navnet antyder, ugjennomsiktig når det gjelder informasjonen den har. Token er bare en identifikator som peker på informasjonen som er lagret på autorisasjonsserveren - den blir validert via introspeksjon på serverens slutt.

2.2. Hva er en ressursserver?

I sammenheng med OAuth 2.0, a ressursserver er et program som beskytter ressurser via OAuth-tokens. Disse tokens blir utstedt av en autorisasjonsserver, vanligvis til et klientprogram. Ressursserverens jobb er å validere tokenet før du serverer en ressurs til klienten.

Et tokens gyldighet bestemmes av flere ting:

  • Kom dette token fra den konfigurerte autorisasjonsserveren?
  • Er den ikke utløpt?
  • Er denne ressursserveren det tiltenkte publikum?
  • Har token den nødvendige myndigheten til å få tilgang til den forespurte ressursen?

For å visualisere, la oss se på et sekvensdiagram for autorisasjonskodestrømmen og se alle aktørene i aksjon:

Som vi kan se i trinn 8, når klientapplikasjonen kaller ressursserverens API for å få tilgang til en beskyttet ressurs, går den først til autorisasjonsserveren for å validere tokenet i forespørselens Autorisasjon: Bærer topptekst, og svarer deretter på klienten.

Trinn 9 er det vi fokuserer på i denne opplæringen.

OK, la oss nå hoppe inn i kodedelen. Vi setter opp en autorisasjonsserver ved hjelp av Keycloak, en ressursserver som validerer JWT-tokens, en annen ressursserver som validerer ugjennomsiktige tokens, og et par JUnit-tester for å simulere klientapper og verifisere svar.

3. Autorisasjonsserver

Først setter vi opp en autorisasjonsserver, eller det som utsteder tokens.

For det vil vi bruke Keycloak innebygd i en Spring Boot Application. Keycloak er en åpen kildekode-identitets- og tilgangshåndteringsløsning. Siden vi fokuserer på ressursserveren i denne opplæringen, vil vi ikke gå dypere inn i den.

Vår innebygde Keycloak Server har to klienter definert - fooClient og barClient - tilsvarer de to ressursserverapplikasjonene våre.

4. Ressursserver - Bruk av JWT

Ressursserveren vår vil ha fire hovedkomponenter:

  • Modell - ressursen for å beskytte
  • API - en REST-kontroller for å avsløre ressursen
  • Sikkerhetskonfigurasjon - en klasse for å definere tilgangskontroll for den beskyttede ressursen som API-en eksponerer
  • application.yml - en konfigurasjonsfil for å erklære egenskaper, inkludert informasjon om autorisasjonsserveren

La oss se dem en etter en for vår ressursserver som håndterer JWT-tokens, etter å ha tatt en titt på avhengighetene.

4.1. Maven avhengigheter

Hovedsakelig trenger vi spring-boot-starter-oauth2-resource-server, Spring Boots startpakke for ressursserverstøtte. Denne starteren inkluderer vårsikkerhet som standard, så vi trenger ikke å legge den eksplisitt til:

 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 

Bortsett fra det, har vi også lagt til nettstøtte.

For demonstrasjonsformålene vil vi generere ressurser tilfeldig i stedet for å hente dem fra en database, med litt hjelp fra Apache's commons-lang3 bibliotek.

4.2. Modell

For å holde det enkelt, vil vi bruke Foo, en POJO, som vår beskyttede ressurs:

offentlig klasse Foo {privat lang id; privat strengnavn; // constructor, getters and setters} 

4.3. API

Her er vår hvilekontroller å lage Foo tilgjengelig for manipulering:

@RestController @RequestMapping (value = "/ foos") offentlig klasse FooController {@GetMapping (value = "/ {id}") offentlig Foo findOne (@PathVariable Long id) {return new Foo (Long.parseLong (randomNumeric (2)) ), tilfeldig Alfabetisk (4)); } @GetMapping offentlig liste findAll () {List fooList = new ArrayList (); fooList.add (ny Foo (Long.parseLong (randomNumeric (2)), randomAlphabetic (4))); fooList.add (ny Foo (Long.parseLong (randomNumeric (2)), randomAlphabetic (4))); fooList.add (ny Foo (Long.parseLong (randomNumeric (2)), randomAlphabetic (4))); returner fooList; } @ResponseStatus (HttpStatus.CREATED) @PostMapping public void create (@RequestBody Foo newFoo) {logger.info ("Foo created"); }}

Som det er tydelig, har vi bestemmelsen om å FÅ alle Foos, FÅ a Foo etter id, og POST a Foo.

4.4. Sikkerhetskonfigurasjon

I denne konfigurasjonsklassen definerer vi tilgangsnivåer for ressursen vår:

@Configuration offentlig klasse JWTSecurityConfig utvider WebSecurityConfigurerAdapter {@Override-beskyttet tomkonfigurasjon (HttpSecurity http) kaster unntak {http .authorizeRequests (authz -> authz .antMatchers (HttpMethod.GET, "/foos/**").hasAhor.hasAhor). .antMatchers (HttpMethod.POST, "/foos").hasAuthority("SCOPE_write") .anyRequest (). authenticated ()) .oauth2ResourceServer (oauth2 -> oauth2.jwt ()); }} 

Alle med tilgangstoken som har lese omfanget kan få Foos. For å POSTE et nytt Foo, token deres skal ha en skrive omfang.

I tillegg vi la til en samtale til jwt () bruker oauth2ResourceServer () DSL for å indikere hvilken type tokens som støttes av serveren vår her.

4.5. application.yml

I applikasjonsegenskapene, i tillegg til vanlig portnummer og kontekstbane, Vi må definere banen til autorisasjonsserverens utsteder-URI slik at ressursserveren kan oppdage leverandørkonfigurasjonen:

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

Ressursserveren bruker denne informasjonen til å validere JWT-tokens som kommer inn fra klientprogrammet, i henhold til trinn 9 i vårt sekvensdiagram.

For at denne valideringen skal fungere ved hjelp av utsteder-uri eiendom, må autorisasjonsserveren være oppe og går. Ellers ville ikke ressursserveren starte.

Hvis vi trenger å starte det uavhengig, så kan vi levere jwk-set-uri eiendom i stedet for å peke på autorisasjonsserverens sluttpunkt som avslører offentlige nøkler:

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

Og det er alt vi trenger for å få serveren vår til å validere JWT-tokens.

4.6. Testing

For testing vil vi sette opp en JUnit. For å utføre denne testen trenger vi autorisasjonsserveren samt ressursserveren oppe og går.

La oss bekrefte at vi kan få Foos fra ressurs-server-jwt med en lese scoped token i vår test:

@Test offentlig ugyldig givenUserWithReadScope_whenGetFooResource_thenSuccess () {String accessToken = obtainAccessToken ("read"); Svarrespons = RestAssured.given () .header (HttpHeaders.AUTHORIZATION, "Bearer" + accessToken) .get ("// localhost: 8081 / resource-server-jwt / foos"); assertThat (respons.as (List.class)). hasSizeGreaterThan (0); }

I den ovennevnte koden, på linje # 3, får vi et tilgangstoken med lese omfang fra autorisasjonsserveren, som dekker trinn 1 til 7 i sekvensdiagrammet vårt.

Trinn 8 utføres av RestAssured‘S få() anrop. Trinn 9 utføres av ressursserveren med konfigurasjonene vi så og er gjennomsiktig for oss som brukere.

5. Ressursserver - bruk av ugjennomsiktige tokens

Deretter, la oss se de samme komponentene for ressursserveren vår som håndterer ugjennomsiktige tokens.

5.1. Maven avhengigheter

For å støtte ugjennomsiktige tokens, trenger vi i tillegg oauth2-oidc-sdk avhengighet:

 com.nimbusds oauth2-oidc-sdk 8.19 kjøretid 

5.2. Modell og kontroller

For denne vil vi legge til en Bar ressurs:

offentlig klasse Bar {privat lang id; privat strengnavn; // constructor, getters and setters} 

Vi får også en BarController med endepunkter som våre FooController før, for å skille ut Bars.

5.3. application.yml

I application.yml her må vi legge til en introspeksjon-uri tilsvarer autorisasjonsserverens introspeksjonsendepunkt. Som nevnt tidligere, blir dette en ugjennomsiktig token validert:

server: 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. Sikkerhetskonfigurasjon

Holde tilgangsnivåene som ligner på Foo for Bar ressurs også, denne konfigurasjonsklassen ringer også til opaqueToken () bruker oauth2ResourceServer () DSL for å indikere bruken av den ugjennomsiktige token-typen:

@Configuration offentlig klasse OpaqueSecurityConfig utvider 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 beskyttet ugyldig konfigurering (HttpSecurity http) kaster unntak {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) .introspectionClientCredentials clientSecret))); }} 

Her spesifiserer vi også klientinformasjonen som tilsvarer autorisasjonsserverens klient vi skal bruke. Vi definerte disse tidligere i vår application.yml.

5.5. Testing

Vi setter opp en JUnit for den ugjennomsiktige tokenbaserte ressursserveren vår, i likhet med hvordan vi gjorde det for JWT-en.

I dette tilfellet, la oss sjekke om a skrive scoped access token kan POSTE a Bar til ressurs-server-ugjennomsiktig:

@Test offentlig ugyldighet givenUserWithWriteScope_whenPostNewBarResource_thenCreated () {String accessToken = obtainAccessToken ("les skriv"); Bar newBar = new Bar (Long.parseLong (randomNumeric (2)), randomAlphabetic (4)); Svarrespons = RestAssured.given () .contentType (ContentType.JSON). Header (HttpHeaders.AUTHORIZATION, "Bearer" + accessToken) .body (newBar) .log () .all () .post ("// localhost: 8082 / resource-server-opaque / bars "); assertThat (response.getStatusCode ()). er EqualTo (HttpStatus.CREATED.value ()); }

Hvis vi får statusen CREATED tilbake, betyr det at ressursserveren validerte det ugjennomsiktige token og opprettet Bar for oss.

6. Konklusjon

I denne veiledningen så vi hvordan vi konfigurerer et Spring Security-basert ressursserverapplikasjon for validering av JWT så vel som ugjennomsiktige tokens.

Som vi så, med minimalt oppsett gjorde Spring det mulig å sømløst validere tokens med en utsteder og sende ressurser til den anmodende parten - i vårt tilfelle en JUnit-test.

Som alltid er kildekoden tilgjengelig på GitHub.


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