Korištenje JWT-a s Spring Security OAuth

1. Pregled

U ovom uputstvu razgovarat ćemo o tome kako postići da naša implementacija Spring Security OAuth2 iskoristi JSON web žetone.

Također nastavljamo nadograđivati ​​članak Spring REST API + OAuth2 + Angular u ovoj seriji OAuth.

2. OAuth2 poslužitelj za autorizaciju

Prije toga, Spring OAuth stog pružao je mogućnost postavljanja Autorizacijskog poslužitelja kao proljetne aplikacije. Tada smo ga morali konfigurirati za upotrebu JwtTokenStore kako bismo mogli koristiti JWT tokene.

Međutim, OAuth stog zastario je do proljeća i sada ćemo koristiti Keycloak kao naš autorizacijski poslužitelj.

Dakle, ovaj put postavit ćemo naš Autorizacijski poslužitelj kao ugrađeni Keycloak poslužitelj u aplikaciji Spring Boot. Prema zadanim postavkama izdaje JWT tokene, tako da nema potrebe za bilo kojom drugom konfiguracijom u tom pogledu.

3. Resursni poslužitelj

Sada, pogledajmo kako konfigurirati naš Resursni poslužitelj za upotrebu JWT-a.

To ćemo učiniti za primjena.iml datoteka:

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

JWT uključuju sve informacije unutar tokena. Dakle, Resource Server mora provjeriti potpis žetona kako bi bio siguran da podaci nisu promijenjeni. The jwk-set-uri imovine sadrži javni ključ koje poslužitelj može koristiti u tu svrhu.

The izdavatelj-uri svojstvo ukazuje na osnovni URI poslužitelja za autorizaciju, koji se također može koristiti za provjeru br zahtjev, kao dodatna sigurnosna mjera.

Također, ako jwk-set-uri svojstvo nije postavljeno, poslužitelj resursa pokušat će koristiti izdavatelj-ui da biste odredili mjesto ovog ključa iz krajnje točke metapodataka poslužitelja autorizacije.

Važno je dodati i izdavatelj-uri imovina nalaže da trebali bismo pokrenuti Autorizacijski poslužitelj prije nego što možemo pokrenuti aplikaciju Resource Server.

Sada da vidimo kako možemo konfigurirati podršku za JWT koristeći Java konfiguraciju:

@Configuration javna klasa SecurityConfig proširuje WebSecurityConfigurerAdapter {@Override zaštićena void konfiguracija (HttpSecurity http) baca izuzetak {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 (); }}

Ovdje nadjačavamo zadanu konfiguraciju Http Security. Dakle, to moramo izričito navesti želimo da se ovo ponaša kao Resource Server i da ćemo koristiti metode tokena pristupa oblikovane u JWT koristeći metode oauth2ResourceServer () i jwt ()odnosno.

Gornja JWT konfiguracija je ono što nam pruža zadana instanca Spring Boot. Ovo se također može prilagoditi, što ćemo vidjeti uskoro.

4. Prilagođena potraživanja u žetonu

Postavimo sada neku infrastrukturu kako bismo mogli dodati nekoliko prilagođena polaganja prava u tokenu pristupa koja je vratio Autorizacijski poslužitelj. Standardna polaganja prava koja pruža okvir su dobra i dobra, ali većinu vremena trebat će nam neke dodatne informacije u tokenu da bismo ih koristili na strani klijenta.

Uzmimo primjer prilagođenog polaganja prava, organizacija, koji će sadržavati naziv date organizacije korisnika.

4.1. Konfiguracija poslužitelja za autorizaciju

Za ovo, moramo dodati nekoliko konfiguracija u našu datoteku definicije carstva, baeldung-carstvo.json:

  • Dodajte atribut organizacija našem korisniku [e-pošta zaštićena]:
    "attributes": {"organization": "baeldung"},
  • Dodati protocolMapper pozvao organizacija prema jwtClient konfiguracija:
    "protocolMappers": [{"id": "06e5fc8f-3553-4c75-aef4-5a4d7bb6c0d1", "name": "organization", "protocol": "openid-connect", "protocolMapper": "oidc-usermodel-attribute -mapper "," obvezan je pristanak ": false," config ": {" userinfo.token.claim ":" true "," user.attribute ":" organization "," id.token.claim ":" true "," access.token.claim ":" true "," zahtev.ime ":" organizacija "," jsonType.label ":" Niz "}}],

Za samostalno postavljanje Keycloak-a to se također može učiniti pomoću Admin console.

Uz to, važno je to upamtiti gornja JSON-ova konfiguracija specifična je za Keycloak i može se razlikovati za ostale OAuth poslužitelje.

S ovom novom konfiguracijom koja je pokrenuta, dobit ćemo dodatni atribut organizacija = baeldung, u korisničkom opterećenju tokena za [e-pošta zaštićena]:

{jti: "989ce5b7-50b9-4cc6-bc71-8f04a639461e" exp: 1585242462 nbf: 0 iat: 1585242162 iss: "// localhost: 8083 / auth / realms / baeldung" sub: "a5461470-33eb-4b2d-82d4ad-82d4ad-82d4ad4 "typ:" Bearer "azp:" jwtClient "auth_time: 1585242162 session_state:" 384ca5cc-8342-429a-879c-c15329820006 "acr:" 1 "scope:" profile write read "organizacija:" baeldung "prefer_username:" [email protected ] "}

4.2. Upotrijebite token pristupa u kutnom klijentu

Dalje ćemo htjeti iskoristiti informacije o žetonu u našoj aplikaciji Angular Client. Za to ćemo upotrijebiti knjižnicu angular2-jwt.

Iskoristit ćemo organizacija tvrde u našem AppServicei dodajte funkciju getOrganization:

getOrganization () {var token = Cookie.get ("access_token"); var payload = this.jwtHelper.decodeToken (token); this.organization = korisni teret.organization; vrati ovo.organizacija; }

Ova funkcija koristi JwtHelperService od kutna2-jwt knjižnica za dekodiranje pristupnog tokena i dobivanje našeg prilagođenog zahtjeva. Sada sve što trebamo je prikazati u našem AppComponent:

@Component ({selector: 'app-root', predložak: `Spring Security Oauth - autorizacijski kod 

{{organizacija}}

`}) klasa izvoza AppComponent implementira OnInit {javna organizacija =" "; konstruktor (privatna usluga: AppService) {} ngOnInit () {this.organization = this.service.getOrganization (); }}

5. Pristupite dodatnim zahtjevima na poslužitelju resursa

Ali, kako možemo pristupiti tim informacijama na strani poslužitelja resursa?

5.1. Pristupite potraživanjima poslužitelja za provjeru autentičnosti

To je doista jednostavno: to jednostavno trebamo izvuci ga iz org.springframework.security.oauth2.jwt.Jwt‘S AutentifikacijaOsnovna, kao što bismo to učinili za bilo koji drugi atribut u UserInfoController:

@GetMapping ("/ user / info") javna karta getUserInfo (@AuthenticationPrincipal Jwt principal) {Map map = new Hashtable (); map.put ("korisničko ime", principal.getClaimAsString ("prefer_username")); map.put ("organizacija", principal.getClaimAsString ("organizacija")); vratiti Collections.unmodifiableMap (karta); } 

5.2. Konfiguracija za dodavanje / uklanjanje / preimenovanje zahtjeva

Sada, što ako želimo dodati više zahtjeva na strani poslužitelja resursa? Ili neke ukloniti ili preimenovati?

Recimo da želimo izmijeniti organizacija potraživanje koje dolazi s autentifikacijskog poslužitelja kako bi se vrijednost dobila velikim slovom. Osim toga, ako polaganje prava nije na korisniku, njegovu vrijednost moramo postaviti kao nepoznata.

Da bismo to postigli, prvo ćemo morati dodajte klasu koja implementira Konverter sučelje i koristi MappedJwtClaimSetConverter pretvoriti potraživanja:

javna klasa OrganizationSubClaimAdapter implementira Converter {privatni konačni MappedJwtClaimSetConverter delegat = MappedJwtClaimSetConverter.withDefaults (Collections.emptyMap ()); javna pretvorba karte (potraživanja na karti) {Map convertClaims = this.delegate.convert (potraživanja); Organizacija niza = convertClaims.get ("organizacija")! = Null? (Niz) convertClaims.get ("organizacija"): "nepoznato"; convertClaims.put ("organizacija", organization.toUpperCase ()); return convertClaims; }}

Drugo, u našem SecurityConfig razred, trebamo dodajte svoje JwtDecoder primjer da nadjača onu koju pruža Spring Boot i postaviti naše OrganizationSubClaimAdapter kao pretvarač potraživanja:

@Bean javni JwtDecoder customDecoder (OAuth2ResourceServerProperties svojstva) {NimbusJwtDecoder jwtDecoder = NimbusJwtDecoder.withJwkSetUri (properties.getJwt (). GetJwkSetU (); getJwkSetU jwtDecoder.setClaimSetConverter (novi OrganizationSubClaimAdapter ()); vratiti jwtDecoder; } 

Sad kad smo pogodili svoje / korisnik / info API za korisnika [e-pošta zaštićena], dobit ćemo organizacija kao NEPOZNATI.

Imajte na umu da je nadjačavanje zadane vrijednosti JwtDecoder bean konfiguriran Spring Bootom treba pažljivo obaviti kako bi se osiguralo da su sve potrebne konfiguracije još uvijek uključene.

6. Učitavanje ključeva iz Java Keystore

U našoj prethodnoj konfiguraciji koristili smo zadani javni ključ poslužitelja za autorizaciju kako bismo provjerili integritet našeg tokena.

Za potpisivanje možemo koristiti i parove ključeva i certifikat pohranjene u datoteci Java Keystore.

6.1. Generirajte JKS Java KeyStore datoteku

Prvo generirajmo ključeve - i točnije a .jks datoteka - pomoću alata za naredbene retke alat za ključeve:

keytool -genkeypair -alias mytest -keyalg RSA -keypass mypass -keystore mytest.jks -storepass mypass

Naredba će generirati datoteku pod nazivom mytest.jks koji sadrži naše ključeve - javni i privatni ključevi.

Također se pobrinite tipkovnica i spremište isti su.

6.2. Izvoz javnog ključa

Dalje, moramo izvesti naš javni ključ iz generiranog JKS-a, za to možemo koristiti sljedeću naredbu:

keytool -list -rfc --keystore mytest.jks | openssl x509 -inform pem -pubkey

Uzorak odgovora izgledat će ovako:

----- ----- početi javni ključ MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAgIK2Wt4x2EtDl41C7vfp OsMquZMyOyteO2RsVeMLF / hXIeYvicKr0SQzVkodHEBCMiGXQDz5prijTq3RHPy2 / 5WJBCYq7yHgTLvspMy6sivXN7NdYE7I5pXo / KHk4nz + Fa6P3L8 + L90E / 3qwf6j3 DKWnAgJFRY8AbSYXt1d5ELiIG1 / gEqzC0fZmNhhfrBtxwWXrlpUDT0Kfvf0QVmPR xxCLXT + tEe1seWGEqeOLL5vXRLqmzZcBe1RZ9kQQm43 + a9Qn5icSRnDfTAesQ3Cr lAWJKl2kcWU1HwJqw + dZRSZ1X4kEXNMyzPdPBbGmU6MHdhpywI7SKZT7mX4BDnUK eQIDAQAB ----- END javni ključ --- - ----- ----- početi certifikat MIIDCzCCAfOgAwIBAgIEGtZIUzANBgkqhkiG9w0BAQsFADA2MQswCQYDVQQGEwJ1 czELMAkGA1UECBMCY2ExCzAJBgNVBAcTAmxhMQ0wCwYDVQQDEwR0ZXN0MB4XDTE2 MDMxNTA4MTAzMFoXDTE2MDYxMzA4MTAzMFowNjELMAkGA1UEBhMCdXMxCzAJBgNV BAgTAmNhMQswCQYDVQQHEwJsYTENMAsGA1UEAxMEdGVzdDCCASIwDQYJKoZIhvcN AQEBBQADggEPADCCAQoCggEBAICCtlreMdhLQ5eNQu736TrDKrmTMjsrXjtkbFXj Cxf4VyHmL4nCq9EkM1ZKHRxAQjIhl0A8 + aa4o06t0Rz8tv + ViQQmKu8h4Ey77KTM urIr1zezXWBOyOaV6Pyh5OJ8 / hWuj9y / Pi / dBP96sH + o9wylpwICRUWPAG0mF7dX eRC4iBtf4BKswtH2ZjYYX6wbccFl65aVA09Cn739EFZj0ccQi10 / rRHtbHlhhKnj i y + b10S6ps2XAXtUWfZEEJuN / mvUJ + YnEkZw30wHrENwq5QFiSpdpHFlNR8CasPn WUUmdV + JBFzTMsz3TwWxplOjB3YacsCO0imU + 5l + AQ51CnkCAwEAAaMhMB8wHQYD VR0OBBYEFOGefUBGquEX9Ujak34PyRskHk + WMA0GCSqGSIb3DQEBCwUAA4IBAQB3 1eLfNeq45yO1cXNl0C1IQLknP2WXg89AHEbKkUOA1ZKTOizNYJIHW5MYJU / zScu0 yBobhTDe5hDTsATMa9sN5CPOaLJwzpWV / ZC6WyhAWTfljzZC6d2rL3QYrSIRxmsp / J1Vq9WkesQdShnEGy7GgRgJn4A8CKecHSzqyzXulQ7Zah6GoEUD + vjb + BheP4aN hiYY1OuXD + HsdKeQqS + 7eM5U7WW6dz2Q8mtFJ5qAxjY75T0pPrHwZMlJUhUZ + Q2V FfweJEaoNB9w9McPe1cAiE + oeejZ0jq0el3 / dJsx3rlVqZN + lMhRJJeVHFyeb3XF lLFCUGhA7hxn2xf3x1JW ----- END POTVRDA-----

6.3. Maven konfiguracija

Dalje, ne želimo da datoteku JKS preuzme postupak filtriranja maven - pa ćemo je isključiti u pom.xml:

   src / main / resources true * .jks 

Ako koristimo Spring Boot, moramo biti sigurni da je naša JKS datoteka dodana u stazu aplikacije putem dodatka Spring Boot Maven - addResources:

   org.springframework.boot spring-boot-maven-plugin true 

6.4. Autorizacijski poslužitelj

Sada ćemo konfigurirati Keycloak tako da koristi naš Keypair od mytest.jks, dodavanjem u JSON datoteke definicije područja KeyProvider odjeljak kako slijedi:

{"id": "59412b8d-aad8-4ab8-84ec-e546900fc124", "name": "java-keystore", "providerId": "java-keystore", "subComponents": {}, "config": {" keystorePassword ": [" mypass "]," keyAlias ​​": [" mytest "]," keyPassword ": [" mypass "]," active ": [" true "]," keystore ": [" src / main / resources /mytest.jks "]," prioritet ": [" 101 "]," omogućeno ": [" istinito "]," algoritam ": [" RS256 "]}},

Ovdje smo postavili prioritet do 101, veći od bilo kojeg drugog paketa ključeva za naš poslužitelj za autorizaciju i postavljen aktivan do pravi. To je učinjeno kako bi se osiguralo da će naš Resursni poslužitelj odabrati upravo ovu pare ključeva s jwk-set-uri svojstvo koje smo ranije naveli.

Opet, ova je konfiguracija specifična za Keycloak i može se razlikovati za ostale implementacije OAuth poslužitelja.

7. Zaključak

U ovom smo se brzom članku usredotočili na postavljanje našeg projekta Spring Security OAuth2 za upotrebu JSON web tokena.

Potpuna implementacija ovog vodiča može se naći u preko na GitHub-u.