Nadopunjavavanje Java provjere autentičnosti pomoću JSON web tokena (JWT)

Spremite se za izgradnju ili borbu sa sigurnom provjerom autentičnosti u vašem Java programu? Niste sigurni u prednosti korištenja tokena (i posebno JSON web tokena) ili kako ih treba rasporediti? Uzbuđen sam što ću vam odgovoriti na ova pitanja i još mnogo toga u ovom vodiču!

Prije nego što uđemo u JSON web žetone (JWT) i JJWT biblioteku (koju je stvorio Stormpathov tehnički direktor, Les Hazlewood, a održava je zajednica suradnika), pokrenimo neke osnove.

1. Autentifikacija nasuprot provjeri autentičnosti

Skup protokola koji aplikacija koristi za potvrdu identiteta korisnika je provjera autentičnosti. Aplikacije tradicionalno zadržavaju identitet putem kolačića sesije. Ova se paradigma oslanja na pohranu ID-ova sesija na strani poslužitelja koja prisiljava programere da kreiraju jedinicu za pohranu sesija koja je jedinstvena i specifična za poslužitelj ili je implementirana kao potpuno zaseban sloj za pohranu sesije.

Autentifikacija tokena razvijena je za rješavanje problema ID-ova sesija na strani poslužitelja nisu, a nisu mogli. Baš kao i tradicionalna provjera autentičnosti, korisnici predstavljaju vjerodajnice koje je moguće provjeriti, ali sada im se izdaje skup tokena umjesto ID-a sesije. Početne vjerodajnice mogu biti standardni par korisničko ime / lozinka, API ključevi ili čak tokeni s druge usluge. (Primjer toga je Stormpath-ova značajka provjere autentičnosti API-ja.)

1.1. Zašto žetoni?

Vrlo jednostavno, upotreba tokena umjesto ID-ova sesija može smanjiti opterećenje vašeg poslužitelja, pojednostaviti upravljanje dozvolama i pružiti bolje alate za podršku distribuiranoj infrastrukturi ili infrastrukturi koja se temelji na oblaku. U slučaju JWT-a, to se prije svega postiže prirodom ovih vrsta tokena bez državljanstva (više o tome u nastavku).

Tokeni nude širok spektar aplikacija, uključujući: sheme zaštite krivotvorenja zahtjeva za više mjesta (CSRF), interakcije OAuth 2.0, ID-ove sesija i (u kolačićima) kao prezentacije za provjeru autentičnosti. U većini slučajeva standardi ne određuju određeni format za tokene. Evo primjera tipičnog CSRF tokena Spring Security u HTML obliku:

Ako pokušate objaviti taj obrazac bez pravog CSRF tokena, dobit ćete odgovor na pogrešku i to je korisnost tokena. Gornji primjer je "nijemi" token. To znači da iz samog tokena nema inherentnog značenja. Ovdje također JWT-ovi čine veliku razliku.

2. Što je u JWT-u?

JWT-ovi (izgovarajući se „jots“) URL-ovi su, kodirani, kriptografski potpisani (ponekad šifrirani) nizovi koji se mogu koristiti kao tokeni u raznim aplikacijama. Evo primjera JWT-a koji se koristi kao CSRF token:

U ovom slučaju možete vidjeti da je token mnogo duži nego u našem prethodnom primjeru. Kao što smo vidjeli i ranije, ako se obrazac pošalje bez tokena, dobit ćete odgovor na pogrešku.

Pa, zašto JWT?

Gornji token kriptografski je potpisan i stoga ga je moguće provjeriti, pružajući dokaz da u njega nije došlo do miješanja. Također, JWT-ovi su kodirani s raznim dodatnim informacijama.

Pogledajmo anatomiju JWT-a kako bismo bolje razumjeli kako iz njega istiskujemo svu tu dobrotu. Možda ste primijetili da postoje tri različita odjeljka odvojena točkama (.):

ZaglavljeeyJhbGciOiJIUzI1NiJ9
Korisni tereteyJqdGkiOiJlNjc4ZjIzMzQ3ZTM0MTBkYjdlNjg3Njc4MjNiMmQ3MCIsImlhdC

I6MTQ2NjYzMzMxNywibmJmIjoxNDY2NjMzMzE3LCJleHAiOjE0NjY2MzY5MTd9

Potpisrgx_o8VQGuDa2AqCHSgVOD5G68Ld_YYM7N7THmvLIKc

Svaki je odjeljak kodiran URL-om base64. To osigurava sigurnu upotrebu u URL-u (više o tome kasnije). Pogledajmo detaljnije svaki odjeljak pojedinačno.

2.1. Zaglavlje

Ako base64 dekodirate zaglavlje, dobit ćete sljedeći JSON niz:

{"alg": "HS256"}

To pokazuje da je JWT potpisan s HMAC-om koristeći SHA-256.

2.2. Korisni teret

Ako dekodirate korisni teret, dobit ćete sljedeći JSON niz (formatiran radi preglednosti):

{"jti": "e678f23347e3410db7e68767823b2d70", "iat": 1466633317, "nbf": 1466633317, "exp": 1466636917}

Kao što vidite, unutar korisnog tereta nalazi se niz ključeva s vrijednostima. Ti se ključevi nazivaju "potraživanja", a JWT specifikacija sadrži sedam od njih navedenih kao "registrirane" potraživanja. Oni su:

brIzdavatelj
podPredmet
audPublika
isprIstek
nbfNe prije
iatIzdan u
jtiJWT ID

Kada gradite JWT, možete podnijeti bilo koja prilagođena polaganja prava koja želite. Gornji popis jednostavno predstavlja polaganja prava koja su rezervirana i u ključu koji se koristi i u očekivanoj vrsti. Naš CSRF ima JWT ID, vrijeme “Issued At”, “Not Before” i Vrijeme isteka. Vrijeme isteka točno je jednu minutu nakon objavljenog vremena.

2.3. Potpis

Konačno, odjeljak s potpisom kreira se uzimajući zaglavlje i korisni teret zajedno (s. Između) i prolazeći ih kroz navedeni algoritam (HMAC koristeći SHA-256, u ovom slučaju) zajedno s poznatom tajnom. Imajte na umu da je tajna u tome stalno bajtni niz i trebao bi biti duljine koja ima smisla za korišteni algoritam. Ispod koristim slučajni niz kodiran s base64 (radi čitljivosti) koji se pretvara u bajtni niz.

Izgleda ovako u pseudo-kodu:

computeHMACSHA256 (zaglavlje + "." + nosivost, base64DecodeToByteArray ("4pE8z3PBoHjnV1AhvGk + e8h2p + ShZpOnpr8cwHmMh1w ="))

Sve dok znate tajnu, možete sami generirati potpis i usporediti svoj rezultat s odjeljkom za potpis JWT-a kako biste potvrdili da nije promijenjen. Tehnički se JWT koji je kriptografski potpisan naziva JWS. JWT se također mogu šifrirati i tada bi se zvali JWE. (U stvarnoj praksi izraz JWT koristi se za opisivanje JWE-a i JWS-a.)

To nas vraća na prednosti korištenja JWT-a kao našeg CSRF tokena. Možemo provjeriti potpis i možemo koristiti podatke kodirane u JWT da bismo potvrdili njegovu valjanost. Dakle, ne samo da niz predstavljanja JWT-a mora odgovarati onome što je pohranjeno na strani poslužitelja, već možemo osigurati da isti ne istekne jednostavnim pregledom ispr zahtjev. To štedi poslužitelj od održavanja dodatnog stanja.

Pa, ovdje smo pokrili puno tla. Zaronimo u neki kod!

3. Postavite Vodič za JJWT

JJWT (//github.com/jwtk/jjwt) je Java knjižnica koja pruža izradu i provjeru JSON web tokena od kraja do kraja. Zauvijek besplatan i otvorenog koda (Apache licenca, verzija 2.0), dizajniran je sa sučeljem usmjerenim na graditelje koji skriva većinu njegove složenosti.

Primarne operacije u korištenju JJWT uključuju izgradnju i raščlanjivanje JWT-ova. Sljedeće ćemo razmotriti ove operacije, zatim ćemo ući u neke proširene značajke JJWT-a, i konačno, vidjet ćemo JWT-ove u akciji kao CSRF tokene u Spring Security, Spring Boot aplikaciji.

Kod prikazan u sljedećim odjeljcima možete pronaći ovdje. Napomena: Projekt od početka koristi Spring Boot kao jednostavnu interakciju s API-jem koji izlaže.

Za izgradnju projekta izvedite sljedeće:

git clone //github.com/eugenp/tutorials.git cd tutorial / jjwt mvn clean install

Jedna od sjajnih stvari u Spring Boot-u je kako je lako pokrenuti aplikaciju. Da biste pokrenuli aplikaciju JJWT Fun, jednostavno učinite sljedeće:

java -jar target / *. jar 

U ovom primjeru aplikacije izloženo je deset krajnjih točaka (koristim httpie za interakciju s aplikacijom. Može se naći ovdje.)

http localhost: 8080
Dostupne naredbe (pod pretpostavkom httpie - //github.com/jkbrzt/httpie): http // localhost: 8080 / Ova poruka o upotrebi http // localhost: 8080 / static-builder build JWT iz tvrdo kodiranih zahtjeva http POST // localhost: 8080 / dynamic-builder-generalni zahtjev-1 = vrijednost-1 ... [zahtjev-n = vrijednost-n] izgraditi JWT iz proslijeđenih zahtjeva (koristeći opću mapu zahtjeva) http POST // localhost: 8080 / dynamic-builder-specific zahtjev -1 = vrijednost-1 ... [zahtjev-n = vrijednost-n] izrada JWT-a iz proslijeđenih zahtjeva (koristeći određene metode zahtjeva) http POST // localhost: 8080 / dynamic-builder-compress zahtjev-1 = vrijednost-1 ... [zahtjeva-n = vrijednost-n] izgraditi DEFLATE komprimirani JWT od proslijeđenih u zahtjevima http // localhost: 8080 / parser? jwt = Analiza proslijeđena u JWT http // localhost: 8080 / parser-force? jwt = Analiza proslijeđena u JWT-u provodeći registrirano polaganje prava na 'iss' i prilagođeno polaganje prava 'hasMotorcycle' http // localhost: 8080 / get-secrets Prikažite trenutno korištene ključeve za potpisivanje. http // localhost: 8080 / refresh-secrets Generirajte nove ključeve za potpisivanje i pokažite ih. http POST // localhost: 8080 / set-secrets HS256 = base64-kodirana vrijednost HS384 = base64-kodirana vrijednost HS512 = base64-kodirana vrijednost Izričito postavljene tajne za upotrebu u aplikaciji.

U odjeljcima koji slijede ispitat ćemo svaku od ovih krajnjih točaka i JJWT kôd sadržan u obrađivačima.

4. Izgradnja JWT-a s JJWT-om

Zbog tečnog sučelja JJWT-a, stvaranje JWT-a u osnovi je postupak u tri koraka:

  1. Definicija internih potraživanja tokena, poput Izdavatelja, Predmeta, Isteka i ID-a.
  2. Kriptografsko potpisivanje JWT-a (što ga čini JWS-om).
  3. Zbijanje JWT-a na niz zaštićen URL-om, u skladu s pravilima JWT Compact Serialization.

Konačni JWT bit će trodijelni niz kodiran osnovom64, potpisan navedenim algoritmom potpisa i koristeći dani ključ. Nakon ovog trenutka token je spreman za dijeljenje s drugom stranom.

Evo primjera JJWT-a u akciji:

Niz jws = Jwts.builder () .setIssuer ("Stormpath") .setSubject ("msilverman") .claim ("name", "Micah Silverman") .claim ("scope", "admins") // Pet 24. lipnja 2016. 15:33:42 GMT-0400 (EDT) .setIssuedAt (Date.from (Instant.ofEpochSecond (1466796822L))) // Sub 24. lipnja 2116 15:33:42 GMT-0400 (EDT) .setExpiration (Date.od (Instant.ofEpochSecond (4622470422L))) .signWith (SignatureAlgorithm.HS256, TextCodec.BASE64.decode ("Yn2kjibddFAWtnPJ2AFlL8WXmohJMCvigQggaEypa5E =")).

Ovo je vrlo slično kodu iz StaticJWTController.fixedBuilder metoda kodnog projekta.

U ovom trenutku vrijedi razgovarati o nekoliko anti-obrazaca povezanih s JWT-ima i potpisivanjem. Ako ste ikada prije vidjeli primjere JWT-a, vjerojatno ste naišli na jedan od ovih scenarija potpisivanja anti-pattern:

  1. .signWith (SignatureAlgorithm.HS256, "secret" .getBytes ("UTF-8"))
  2. .signWith (SignatureAlgorithm.HS256, "Yn2kjibddFAWtnPJ2AFlL8WXmohJMCvigQggaEypa5E =". getBytes ("UTF-8"))
  3. .signWith (SignatureAlgorithm.HS512, TextCodec.BASE64.decode ("Yn2kjibddFAWtnPJ2AFlL8WXmohJMCvigQggaEypa5E ="))

Bilo koji od HS algoritmi za potpis tipa uzimaju bajtni niz. Ljudima je prikladno čitati kako bi uzeli niz i pretvorili ga u bajtni niz.

To pokazuje gornji anti-obrazac 1. To je problematično jer je tajna oslabljena tako kratkom robom i nije bajtni niz u svom izvornom obliku. Dakle, kako bi bilo čitljivo, možemo base64 kodirati bajtni niz.

Međutim, gornji anti-obrazac 2 uzima kodirani niz base64 i pretvara ga izravno u bajtni niz. Ono što treba učiniti je dekodirati niz base64 natrag u izvorni bajtni niz.

Broj 3 gore to pokazuje. Pa, zašto je ovaj također anti-obrazac? To je suptilan razlog u ovom slučaju. Primijetite da je algoritam potpisa HS512. Niz bajtova nije najveća duljina koja HS512 može podržati, čineći ga slabijom tajnom od onoga što je moguće za taj algoritam.

Primjer koda uključuje klasu tzv Tajna služba koji osigurava da se za zadani algoritam koriste tajne odgovarajuće čvrstoće. U vrijeme pokretanja aplikacije stvara se novi set tajni za svaki od HS algoritama. Postoje krajnje točke za osvježavanje tajni, kao i za izričito postavljanje tajni.

Ako imate pokrenut projekt kako je gore opisano, izvedite sljedeće tako da primjeri JWT u nastavku odgovaraju odgovorima iz vašeg projekta.

http POST hostu: 8080 / set tajne \ HS256 = "Yn2kjibddFAWtnPJ2AFlL8WXmohJMCvigQggaEypa5E =" \ HS384 = "VW96zL + tYlrJLNCQ0j6QPTp + d1q75n / Wa8LVvpWyG8pPZOP6AA5X7XOIlI90sDwx" \ HS512 = "cd + Pr1js + w2qfT2BoCD + tPcYp9LbjpmhSMEJqUob1mcxZ7 + Wmik4AYdjX + DlDjmE4yporzQ9tm7v3z / j + QbdYg =="

Sada možete pritisnuti / statički graditelj krajnja točka:

http // localhost: 8080 / static-builder

Ovo stvara JWT koji izgleda ovako:

eyJhbGciOiJIUzI1NiJ9. eyJpc3MiOiJTdG9ybXBhdGgiLCJzdWIiOiJtc2lsdmVybWFuIiwibmFtZSI6Ik1pY2FoIFNpbHZlcm1hbiIsInNjb3BlIjoiYM0JIJJJJJYJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJ0JІЙ kP0i_RvTAmI8mgpIkDFhRX3XthSdP-eqqFKGcU92ZIQ

Sad, pogodi:

http //localhost:8080/parser?jwt=eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJTdG9ybXBhdGgiLCJzdWIiOiJtc2lsdmVybWFuIiwibmFtZSI6Ik1pY2FoIFNpbHZlcm1hbiIsInNjb3BlIjoiYWRtaW5zIiwiaWF0IjoxNDY2Nzk2ODIyLCJleHAiOjQ2MjI0NzA0MjJ9.kP0i_RvTAmI8mgpIkDFhRX3XthSdP-eqqFKGcU92ZIQ

Odgovor sadrži sve tvrdnje koje smo uključili prilikom stvaranja JWT-a.

HTTP / 1.1 200 OK Tip sadržaja: application / json; charset = UTF-8 ... {"jws": {"body": {"exp": 4622470422, "iat": 1466796822, "iss": "Stormpath "," name ":" Micah Silverman "," scope ":" admins "," sub ":" msilverman "}," header ": {" alg ":" HS256 "}," signature ":" kP0i_RvTAmI8mgpIkDFhRX3XthSdP-eqqFKGcU92ZIQ "}," status ":" USPJEH "}

Ovo je operacija raščlanjivanja, u koju ćemo ući u sljedećem odjeljku.

Hajdemo sada pogoditi krajnju točku koja uzima potraživanja kao parametre i izradit će prilagođeni JWT za nas.

http -v POST localhost: 8080 / dynamic-builder-general iss = Stormpath sub = msilverman hasMotorcycle: = true

Bilješka: Postoji suptilna razlika između imaMotocikl potraživanja i ostale potraživanja. httpie pretpostavlja da su JSON parametri prema zadanim postavkama nizovi. Da biste poslali sirovi JSON pomoću httpie, koristite := oblik, a ne =. Bez toga bi se podvrglo “HasMotorcycle”: „true”, što nije ono što želimo.

Evo rezultata:

POST / dynamic-builder-general HTTP / 1.1 Prihvaćam: application / json ... {"hasMotorcycle": true, "iss": "Stormpath", "sub": "msilverman"} HTTP / 1.1 200 OK Content Type: primjena / json; charset = UTF-8 ... { "JWT": "eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJTdG9ybXBhdGgiLCJzdWIiOiJtc2lsdmVybWFuIiwiaGFzTW90b3JjeWNsZSI6dHJ1ZX0.OnyDs-zoL3-rw1GaSl_KzZzHK9GoiNocu-YwZ_nQNZU", "stanje": "uspjeh"} 

Pogledajmo kod koji podupire ovu krajnju točku:

@RequestMapping (value = "/ dynamic-builder-general", method = POST) javni JwtResponse dynamicBuilderGeneric (@RequestBody Map tvrdi) baca UnsupportedEncodingException {String jws = Jwts.builder () .setClaims (potražnja) .signWith (SignatureA25 secretService.getHS256SecretBytes ()) .compact (); vratiti novi JwtResponse (jws); }

Linija 2 osigurava da se dolazni JSON automatski pretvori u Java kartu, što je vrlo zgodno za JJWT, jer metoda na retku 5 jednostavno uzima tu kartu i postavlja sve tvrdnje odjednom.

Koliko god je ovaj kod kratak, trebamo nešto konkretnije kako bismo osigurali da su usvojeni zahtjevi valjani. Koristiti .setClaims (zahtjevi na karti) metoda je zgodna kada već znate da su potraživanja predstavljena na karti valjana. Ovo je mjesto gdje sigurnost Java-a dolazi u JJWT knjižnicu.

Za svako registrirano polaganje prava definirano u specifikaciji JWT, postoji odgovarajuća Java metoda u JJWT koja uzima tip sa točnošću specifikacije.

Idemo na drugu krajnju točku u našem primjeru i vidjeti što će se dogoditi:

http -v POST localhost: 8080 / specific-builder-specific iss = Stormpath sub: = 5 hasMotorcycle: = true

Imajte na umu da smo predali cijeli broj 5 za zahtjev za "pod". Evo rezultata:

POST / dynamic-builder-specific HTTP / 1.1 Accept: application / json ... {"hasMotorcycle": true, "iss": "Stormpath", "sub": 5} HTTP / 1.1 400 Loša veza zahtjeva: zatvori Content- Tip: application / json; charset = UTF-8 ... {"exceptionType": "java.lang.ClassCastException", "message": "java.lang.Integer se ne može prebaciti na java.lang.String", "status ":" POGREŠKA "}

Sada dobivamo odgovor na pogrešku jer kod primjenjuje tip Registriranih zahtjeva. U ovom slučaju, pod mora biti niz. Evo koda koji podupire ovu krajnju točku:

@RequestMapping (value = "/ specific-builder-specific", method = POST) javni JwtResponse dynamicBuilderSpecific (tvrdnje @RequestBody Map) baca UnsupportedEncodingException {JwtBuilder builder = Jwts.builder (); zahtjeva.forEach ((ključ, vrijednost) -> {prekidač (ključ) {slučaj "iss": builder.setIssuer ((niz) vrijednost); prekid; slučaj "sub": builder.setSubject ((niz) vrijednost); prekid ; case "aud": builder.setAudience ((String) value); break; case "exp": builder.setExpiration (Date.from (Instant.ofEpochSecond (Long.parseLong (value.toString ())))); break ; slučaj "nbf": builder.setNotBefore (Date.from (Instant.ofEpochSecond (Long.parseLong (value.toString ()))))); break; case "iat": builder.setIssuedAt (Date.from (Instant.ofEpochSecond (Long.parseLong (value.toString ())))); break; case "jti": builder.setId ((String) value); break; default: builder.claim (ključ, vrijednost);}}); builder.signWith (SignatureAlgorithm.HS256, secretService.getHS256SecretBytes ()); vrati novi JwtResponse (builder.compact ()); }

Kao i prije, metoda prihvaća a Karta zahtjeva kao njegov parametar. Međutim, ovaj put pozivamo određenu metodu za svaku od registriranih zahtjeva koja primjenjuje tip.

Jedno je poboljšanje u tome da poruka o pogrešci bude preciznija. Trenutno znamo samo da jedna od naših tvrdnji nije ispravna. Ne znamo koja je tvrdnja bila pogreškom ili koja bi trebala biti. Evo metode koja će nam dati konkretniju poruku o pogrešci. Također se bavi greškom u trenutnom kodu.

private void secureType (String registeredClaim, vrijednost objekta, klasa očekujeType) {boolean isCorrectType = očekuje seType.isInstance (vrijednost) || očekivani tip == Long.class && value instanceof Integer; if (! isCorrectType) {String msg = "Očekivani tip:" + očekuje seType.getCanonicalName () + "za registrirani zahtjev: '" + registeredClaim + "', ali je dobio vrijednost:" + vrijednost + "tipa:" + vrijednost. getClass (). getCanonicalName (); baciti novi JwtException (msg); }}

Redak 3 provjerava je li prenesena vrijednost očekivanog tipa. Ako nije, a JwtException baca se s određenom pogreškom. Pogledajmo ovo na djelu uputivši isti poziv kao i ranije:

http -v POST localhost: 8080 / specific-builder-specific iss = Stormpath sub: = 5 hasMotorcycle: = true
POST / Dynamic-builder-specific HTTP / 1.1 Prihvaćam: application / json ...Korisnički agent: HTTPie / 0.9.3 {"hasMotorcycle": true, "iss": "Stormpath", "sub": 5} Veza HTTP / 1.1 400 lošeg zahtjeva: zatvori Content-Type: application / json; charset = UTF -8 ... {"exceptionType": "io.jsonwebtoken.JwtException", "message": "Očekivani tip: java.lang.String za registrirano polaganje prava: 'sub', ali dobivena vrijednost: 5 od tipa: java.lang .Integer "," status ":" POGREŠKA "}

Sad imamo vrlo specifičnu poruku pogreške koja nam govori da pod tvrdnja je ona koja je pogreška.

Krenimo natrag na onu grešku u našem kodu. Izdanje nema nikakve veze s JJWT knjižnicom. Problem je u tome što je mapiranje JSON to Java Object ugrađeno u Spring Boot previše pametno za naše dobro.

Ako postoji metoda koja prihvaća Java objekt, JSON mapper automatski će pretvoriti prosljeđeni broj koji je manji ili jednak 2.147.483.647 u Java Cijeli broj. Isto tako, automatski će pretvoriti proslijeđeni broj veći od 2.147.483.647 u Javu Dugo. Za iat, nbf, i ispr potraživanja JWT-a, želimo da naš test verifyType prođe je li mapirani Objekt Integer ili Long. Zbog toga imamo dodatnu klauzulu u određivanju je li prenesena vrijednost ispravnog tipa:

 boolean isCorrectType = očekuje seType.isInstance (vrijednost) || očekivani tip == Long.class && value instanceof Integer;

Ako očekujemo Long, ali vrijednost je primjer Integer, i dalje kažemo da je to ispravna vrsta. S razumijevanjem što se događa s ovom provjerom valjanosti, sada je možemo integrirati u svoju dynamicBuilderSpecific metoda:

@RequestMapping (value = "/ specific-builder-specific", method = POST) javni JwtResponse dynamicBuilderSpecific (tvrdnje @RequestBody Map) baca UnsupportedEncodingException {JwtBuilder builder = Jwts.builder (); zahtjeva.forEach ((ključ, vrijednost) -> {prekidač (ključ) {slučaj "iss": sureType (ključ, vrijednost, String.class); builder.setIssuer ((String) vrijednost); prekid; slučaj "sub": sureType (ključ, vrijednost, String.class); builder.setSubject ((String) vrijednost); break; slučaj "aud": verifyType (key, value, String.class); builder.setAudience ((String) value); break ; slučaj "exp": osiguratiType (ključ, vrijednost, Long.class); builder.setExpiration (Date.from (Instant.ofEpochSecond (Long.parseLong (value.toString ()))))); break; case "nbf": secureType (ključ, vrijednost, Long.class); builder.setNotBefore (Date.from (Instant.ofEpochSecond (Long.parseLong (value.toString ())))); break; case "iat": secureType (key, value, Long.class); builder.setIssuedAt (Date.from (Instant.ofEpochSecond (Long.parseLong (value.toString ())))); break; case "jti": secureType (key, value, String.class); builder .setId ((String) vrijednost); break; zadana vrijednost: builder.claim (ključ, vrijednost);}}); builder.signWith (SignatureAlgorithm.HS256, secretService.getHS256SecretBytes ()); vrati novi JwtResponse (builder.compact ()); }

Bilješka: U svim primjerima koda u ovom odjeljku JWT-ovi su potpisani HMAC-om pomoću algoritma SHA-256. Ovo radi jednostavnosti primjera. JJWT knjižnica podržava 12 različitih algoritama potpisa koje možete iskoristiti u vlastitom kodu.

5. Raščlanjivanje JWT-ova s ​​JJWT-om

Ranije smo vidjeli da naš primjer koda ima krajnju točku za raščlanjivanje JWT-a. Postizanje ove krajnje točke:

http //localhost:8080/parser?jwt=eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJTdG9ybXBhdGgiLCJzdWIiOiJtc2lsdmVybWFuIiwibmFtZSI6Ik1pY2FoIFNpbHZlcm1hbiIsInNjb3BlIjoiYWRtaW5zIiwiaWF0IjoxNDY2Nzk2ODIyLCJleHAiOjQ2MjI0NzA0MjJ9.kP0i_RvTAmI8mgpIkDFhRX3XthSdP-eqqFKGcU92ZIQ

proizvodi ovaj odgovor:

HTTP / 1.1 200 OK Content-type: application / json; charset = UTF-8 ... {"tvrdnje": {"body": {"exp": 4622470422, "iat": 1466796822, "iss": "Stormpath "," name ":" Micah Silverman "," scope ":" admins "," sub ":" msilverman "}," header ": {" alg ":" HS256 "}," signature ":" kP0i_RvTAmI8mgpIkDFhRX3XthSdP-eqqFKGcU92ZI "}," status ":" USPJEH "}

The parser metoda StaticJWTController razred izgleda ovako:

@RequestMapping (value = "/ parser", method = GET) javni JwtResponse parser (@RequestParam String jwt) baca UnsupportedEncodingException {Jws jws = Jwts.parser () .setSigningKeyResolver (secretService.getSigningKeyRes. vratiti novi JwtResponse (jws); }

Redak 4 označava da očekujemo da će dolazni niz biti potpisani JWT (JWS). I mi koristimo istu tajnu koja je korištena za potpisivanje JWT-a pri njegovom raščlanjivanju. Redak 5 analizira zahtjeve iz JWT-a. Interno provjerava potpis i izbacit će iznimku ako je potpis nevaljan.

Primijetite da u ovom slučaju prolazimo kroz SigningKeyResolver nego sam ključ. Ovo je jedan od najsnažnijih aspekata JJWT-a. Zaglavlje JWT-a označava algoritam koji se koristi za njegovo potpisivanje. Međutim, moramo provjeriti JWT prije nego što mu povjerujemo. Činilo bi se da je ulov 22. Pogledajmo SecretService.getSigningKeyResolver metoda:

private SigningKeyResolver signKeyResolver = new SigningKeyResolverAdapter () {@Override javni bajt [] resolSigningKeyBytes (JwsHeader zaglavlje, potraživanja) {return TextCodec.BASE64.decode (secrets.get (header.getAlgorithm }};

Korištenje pristupa JwsHeader, Mogu pregledati algoritam i vratiti odgovarajući bajtni niz za tajnu koja je korištena za potpisivanje JWT-a. Sada će JJWT provjeriti je li JWT promijenjen pomoću ovog bajt polja kao ključa.

Ako uklonim posljednji znak prosljeđenog u JWT (koji je dio potpisa), ovo je odgovor:

HTTP / 1.1 400 Pogrešna veza zahtjeva: zatvori Vrsta sadržaja: application / json; charset = UTF-8 Datum: ponedjeljak, 27. lipnja 2016. 13:19:08 GMT Poslužitelj: Apache-Coyote / 1.1 Prijenos-kodiranje: kunkirano {"iznimkaTip ":" io.jsonwebtoken.SignatureException "," message ":" JWT potpis se ne podudara s lokalno izračunatim potpisom. JWT valjanost ne može se tvrditi i ne treba joj vjerovati. "," status ":" ERROR "}

6. JWT-ovi u praksi: CSRF žetoni proljetnog osiguranja

Iako fokus ovog posta nije proljetna sigurnost, ovdje ćemo se malo pozabaviti njime kako bismo prikazali neke stvarne upotrebe knjižnice JJWT.

Krivotvorenje zahtjeva za više web lokacija sigurnosna je ranjivost kojom vas zlonamjerna web lokacija zavara da podnesete zahtjeve za web stranicu s kojom ste uspostavili povjerenje. Jedan od uobičajenih lijekova za to je primjena uzorka tokena sinkronizacije. Ovaj pristup ubacuje žeton u web obrazac, a aplikacijski poslužitelj provjerava dolazni token u svom spremištu kako bi potvrdio da je točan. Ako token nedostaje ili je nevaljan, poslužitelj će odgovoriti pogreškom.

Spring Security ima ugrađen obrazac tokena sinkronizacije. Još bolje, ako koristite predloške Spring Boot i Thymeleaf, sinhronizacijski token automatski se umeće za vas.

Po zadanom je token koji koristi Spring Security "nijemi" token. To je samo niz slova i brojeva. Ovaj pristup je sasvim u redu i djeluje. U ovom odjeljku poboljšavamo osnovnu funkcionalnost korištenjem JWT-a kao tokena. Pored provjere da li je poslani token onaj očekivani, validiramo JWT kako bismo dodatno dokazali da token nije prekršen i osigurali da nije istekao.

Za početak ćemo konfigurirati Spring Security pomoću Java konfiguracije. Prema zadanim postavkama, sve staze zahtijevaju provjeru autentičnosti, a sve POST krajnje točke CSRF tokene. To ćemo malo opustiti, tako da ono što smo do sada izgradili i dalje djeluje.

@Configuration javna klasa WebSecurityConfig proširuje WebSecurityConfigurerAdapter {private String [] ignoreCsrfAntMatchers = {"/ dynamic-builder-compress", "/ dynamic-builder-general", "/ dynamic-builder-specific", "/ set-secrets"}; @Override zaštićena void konfiguracija (HttpSecurity http) baca izuzetak {http .csrf () .ignoringAntMatchers (ignoreCsrfAntMatchers) .and (). AuthiteRequests () .antMatchers ("/ **") .permitAll (); }}

Ovdje radimo dvije stvari. Prvo, kažemo da su CSRF tokeni ne potrebno prilikom postavljanja na naše krajnje točke REST API (redak 15). Drugo, kažemo da bi za sve staze trebao biti dopušten neautentificirani pristup (linije 17 - 18).

Potvrdimo da Spring Security radi onako kako mi očekujemo. Pokrenite aplikaciju i pritisnite ovaj URL u pregledniku:

// localhost: 8080 / jwt-csrf-form

Evo predloška majčine dušice za ovaj prikaz:

Ovo je vrlo osnovni obrazac koji će poslati na istu krajnju točku kada se pošalje. Primijetite da u obrascu nema izričite reference na CSRF tokene. Ako pogledate izvor, vidjet ćete nešto poput:

Ovo je sve potvrda koju trebate znati da Spring Security funkcionira i da predlošci Thymeleaf automatski ubacuju CSRF token.

Da bi vrijednost bila JWT, omogućit ćemo prilagođavanje CsrfTokenRepository. Evo kako se mijenja naša konfiguracija Spring Security:

@Configuration javna klasa WebSecurityConfig proširuje WebSecurityConfigurerAdapter {@Autowired CsrfTokenRepository jwtCsrfTokenRepository; @Override zaštićena void konfiguracija (HttpSecurity http) baca izuzetak {http .csrf () .csrfTokenRepository (jwtCsrfTokenRepository) .ignoringAntMatchers (ignoreCsrfAntMatchers) .and (). AuthzeRequests (). ** (Ant. }}

Da bismo to povezali, potrebna nam je konfiguracija koja izlaže grah koji vraća prilagođeno spremište tokena. Evo konfiguracije:

@Configuration javna klasa CSRFConfig {@Autowired SecretService secretService; @Bean @ConditionalOnMissingBean public CsrfTokenRepository jwtCsrfTokenRepository () {return new JWTCsrfTokenRepository (secretService.getHS256SecretBytes ()); }}

I, evo našeg prilagođenog spremišta (važni bitovi):

javna klasa JWTCsrfTokenRepository implementira CsrfTokenRepository {private static final Logger log = LoggerFactory.getLogger (JWTCsrfTokenRepository.class); privatni bajt [] tajna; javni JWTCsrfTokenRepository (bajt [] tajna) {this.secret = tajna; } @Override public CsrfToken generirajToken (HttpServletRequest zahtjev) {String id = UUID.randomUUID (). ToString (). Replace ("-", ""); Datum sada = novi datum (); Datum isteka = novi datum (System.currentTimeMillis () + (1000 * 30)); // 30 sekundi Token string; pokušajte {token = Jwts.builder () .setId (id) .setIssuedAt (now) .setNotBefore (now) .setExpiration (exp) .signWith (SignatureAlgorithm.HS256, secret) .compact (); } catch (UnsupportedEncodingException e) {log.error ("Nije moguće stvoriti CSRf JWT: {}", e.getMessage (), e); žeton = id; } vratiti novi DefaultCsrfToken ("X-CSRF-TOKEN", "_csrf", žeton); } @Override public void saveToken (CsrfToken token, HttpServletRequest zahtjev, HttpServletResponse response) {...} @Override public CsrfToken loadToken (HttpServletRequest zahtjev) {...}}

The generirajToken metoda stvara JWT koji istječe 30 sekundi nakon što je stvoren. S ovom vodovodnom instalacijom možemo ponovno pokrenuti aplikaciju i pogledati izvor / jwt-csrf-obrazac.

Sada, skriveno polje izgleda ovako:

Huzzah! Sada je naš CSRF token JWT. To nije bilo preteško.

Međutim, ovo je samo polovica slagalice. Prema zadanim postavkama Spring Security jednostavno sprema CSRF token i potvrđuje da se token predan u web obrascu podudara s onim koji je spremljen. Želimo proširiti funkcionalnost za provjeru valjanosti JWT-a i osigurati da nije istekao. Da bismo to učinili, dodati ćemo filtar. Evo kako sada izgleda naša konfiguracija Spring Security:

@Configuration javna klasa WebSecurityConfig proširuje WebSecurityConfigurerAdapter {... @Override zaštićena voidna konfiguracija (HttpSecurity http) baca izjemo {http .addFilterAfter (new JwtCsrfValidatorFilter (), CsrfFilterr. .and (). odobritiRequests () .antMatchers ("/ **") .permitAll (); } ...}

U redak 9 dodali smo filtar i stavljamo ga u lanac filtara nakon zadanog CsrfFilter. Dakle, do trenutka kada naš filter pogodi, JWT token (kao cjelina) već će biti potvrđeno da je točna vrijednost koju je spremilo Spring Security.

Evo JwtCsrfValidatorFilter (privatno je jer je unutarnja klasa naše konfiguracije Spring Security):

privatna klasa JwtCsrfValidatorFilter proširuje OncePerRequestFilter {@Override protected void doFilterInternal (HttpServletRequest response, HttpServletResponse response, FilterChain filterChain) baca ServletException, IOExcen to is request.getAttribute ("_ csrf"); if (// briga samo ako se radi o POST "POST" .equals (request.getMethod ()) && // ignoriraj ako je put zahtjeva na našem popisu Arrays.binarySearch (ignoreCsrfAntMatchers, request.getServletPath ()) <0 && / / pobrinite se da imamo token token! = null) {// CsrfFilter se već pobrinuo da se token podudara. // Ovdje ćemo provjeriti nije li istekao, pokušajte {Jwts.parser () .setSigningKey (secret.getBytes ("UTF-8")) .parseClaimsJws (token.getToken ()); } catch (JwtException e) {// najvjerojatnije ExpiredJwtException, ali ovo će obraditi svaki request.setAttribute ("iznimka", e); response.setStatus (HttpServletResponse.SC_BAD_REQUEST); RequestDispatcher dispečer = request.getRequestDispatcher ("expired-jwt"); dispečer.naprijed (zahtjev, odgovor); }} filterChain.doFilter (zahtjev, odgovor); }}

Pogledajte redak 23 na. Analiziramo JWT kao i prije. U ovom slučaju, ako se izuzme izuzetak, zahtjev se prosljeđuje istekao-jwt predložak. Ako JWT provjeri valjanost, obrada se nastavlja kao i obično.

Ovo zatvara petlju nadjačavanja zadanog ponašanja CSRF tokena Spring Security sa spremištem i validatorom JWT tokena.

Ako pokrenete aplikaciju, pregledajte / jwt-csrf-obrazac, pričekajte malo više od 30 sekundi i kliknite gumb, vidjet ćete nešto poput ovoga:

7. Proširene značajke JJWT

Završit ćemo naše JJWT putovanje riječju o nekim značajkama koje nadilaze specifikaciju.

7.1. Izvršiti zahtjeve

Kao dio postupka raščlanjivanja, JJWT vam omogućuje da odredite tražene tvrdnje i vrijednosti koje bi te tvrdnje trebale imati. Ovo je vrlo zgodno ako u vašim JWT-ima postoje određene informacije koje moraju biti prisutne kako biste ih mogli smatrati valjanima. Izbjegava puno logike grananja za ručnu provjeru valjanosti zahtjeva. Evo metode koja služi za / parser-prisiliti krajnja točka našeg uzorka projekta.

@RequestMapping (value = "/ parser-force", method = GET) javni JwtResponse parserEnforce (@RequestParam String jwt) baca UnsupportedEncodingException {Jws jws = Jwts.parser () .requireIssuer ("Stormpath" ,Motor " true) .setSigningKeyResolver (secretService.getSigningKeyResolver ()) .parseClaimsJws (jwt); vratiti novi JwtResponse (jws); }

Redci 5 i 6 prikazuju vam sintaksu registriranih zahtjeva kao i prilagođenih zahtjeva. U ovom će se primjeru JWT smatrati nevaljanim ako zahtjev za iss nije prisutan ili nema vrijednost: Stormpath. Također će biti nevaljano ako prilagođeni zahtjev hasMotorcycle nije prisutan ili nema vrijednost: true.

Stvorimo prvo JWT koji slijedi sretni put:

http -v POST localhost: 8080 / dynamic-builder-specific \ iss = Stormpath hasMotorcycle: = true sub = msilverman
POST / dynamic-builder-specific HTTP / 1.1 Accept: application / json ... {"hasMotorcycle": true, "iss": "Stormpath", "sub": "msilverman"} HTTP / 1.1 200 OK Cache Control: no-cache, ne-trgovina, max-age = 0, potrebno ponovne potvrde Content-Type: application / JSON; charset = UTF-8 ... { "JWT": „status "eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJTdG9ybXBhdGgiLCJoYXNNb3RvcmN5Y2xlIjp0cnVlLCJzdWIiOiJtc2lsdmVybWFuIn0.qrH-U6TLSVlHkZdYuqPRDtgKNr1RilFYQJtJbcgwhR0" ": "USPJEH" }

Sada, provjerimo taj JWT:

http -v localhost: 8080 / rastavljač-provesti JWT = eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJTdG9ybXBhdGgiLCJoYXNNb3RvcmN5Y2xlIjp0cnVlLCJzdWIiOiJtc2lsdmVybWFuIn0.qrH-U6TLSVlHkZdYuqPRDtgKNr1RilFYQJtJbcgwhR0?
Get / rastavljač-nametnuti JWT = http -v localhost: 8080 / rastavljač-nametnuti JWT = eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJTdG9ybXBhdGgiLCJoYXNNb3RvcmN5Y2xlIjp0cnVlLCJzdWIiOiJtc2lsdmVybWFuIn0.qrH-U6TLSVlHkZdYuqPRDtgKNr1RilFYQJtJbcgwhR0 HTTP / 1.1 Prihvati: * / * ... HTTP / 1.1 200 OK Cache-Control: no- predmemorija, ne-trgovina, max-age = 0, mora ponovno potvrditi vrstu sadržaja: application / json; charset = UTF-8 ... {"jws": {"body": {"hasMotorcycle": true, "iss ":" Stormpath "," sub ":" msilverman "}," zaglavlje ": {" alg ":" HS256 "}," potpis ":" qrH-U6TLSVlHkZdYuqPRDtgKNr1RilFYQJtJbcgwhR0 "}," status ":" SUCCESS "

Zasada je dobro. Sada, ovaj put, ostavimo hasMotorcycle van:

http -v POST localhost: 8080 / specific-builder-specific iss = Stormpath sub = msilverman

Ovaj put, ako pokušamo potvrditi JWT:

http -v localhost: 8080 / parser-prisile? jwt = eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJTdG9ybXBhdGgiLCJzdWIiOiJtc2lsdmVybWFuIn0.YMONlFM1tNgttUYcddWGQAYKddgGAUkdBYXdYgAU

dobivamo:

Get / rastavljač-nametnuti JWT = http -v localhost: 8080 / rastavljač-nametnuti JWT = eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJTdG9ybXBhdGgiLCJzdWIiOiJtc2lsdmVybWFuIn0.YMONlFM1tNgttUYukDRsi9gKIocxdGAOLaJBymaQAWc HTTP / 1.1 Prihvati: * / * ... HTTP / 1.1 400 Bad Request Cache-Control: no-cache memorije , no-store, max-age = 0, mora ponovno potvrditi vezu: zatvori Content-Type: application / json; charset = UTF-8 ... {"exceptionType": "io.jsonwebtoken.MissingClaimException", "message": "Očekuje se da je tvrdnja hasMotorcycle: true, ali nije bila prisutna u JWT zahtjevima.", "Status": "ERROR"}

To ukazuje na to da se naša tvrdnja hasMotorcycle očekivala, ali je nedostajala.

Učinimo još jedan primjer:

http -v POST localhost: 8080 / dynamic-builder-specific iss = Stormpath hasMotorcycle: = false sub = msilverman

Ovaj put je prisutno traženo potraživanje, ali ima pogrešnu vrijednost. Pogledajmo rezultate:

http -v localhost: 8080 / parser-provođenje? jwt = eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJTdG9ybXBhdGgiLCJoYXNNb3RvcmN5Y2xlIjpmYWxzZNVBBNWBNWBNWBNVBBVJBB8BWBBJBJBB8BGBJBB0BBK0BBK8BBK0WBBWBWWBWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWKWW
GET / parser-nametnuti JWT = http -v localhost: 8080 / rastavljač-nametnuti JWT = eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJTdG9ybXBhdGgiLCJoYXNNb3RvcmN5Y2xlIjpmYWxzZSwic3ViIjoibXNpbHZlcm1hbiJ9.8LBq2f0eINB34AzhVEgsln_KDo-IyeM8kc-dTzSCr0c HTTP / 1.1 Prihvati: * / * ...HTTP / 1.1 400 Loša kontrola predmemorije zahtjeva: no-cache, no-store, max-age = 0, mora ponovno potvrditi vezu: zatvori Content-Type: application / json; charset = UTF-8 ... {"exceptionType" : "io.jsonwebtoken.IncorrectClaimException", "message": "Očekivana tvrdnja hasMotorcycle da je: true, ali je bila: false.", "status": "ERROR"}

To ukazuje na to da je naša tvrdnja hasMotorcycle bila prisutna, ali da je imala vrijednost koja se nije očekivala.

MissingClaimException i IncorrectClaimException su vam prijatelji prilikom provođenja zahtjeva u vašim JWT-ima i značajka koju ima samo JJWT knjižnica.

7.2. JWT kompresija

Ako imate puno zahtjeva za potraživanje JWT-a, on može postati velik - toliko velik da možda neće stati u GET url u nekim preglednicima.

Napravimo veliki JWT:

http -v POST localhost: 8080 / specific-builder-specific \ iss = Stormpath hasMotorcycle: = true sub = msilverman the = quick brown = lisica je skočila = preko lijenog = pas \ negdje = preko duge = put gore = visoko i = snovi = sanjali ste = o

Evo JWT-a koji proizvodi:

eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJTdG9ybXBhdGgiLCJoYXNNb3RvcmN5Y2xlIjp0cnVlLCJzdWIiOiJtc2lsdmVybWFuIiwidGhlIjoicXVpY2siLCJicm93biI6ImZveCIsImp1bXBlZCI6Im92ZXIiLCJsYXp5IjoiZG9nIiwic29tZXdoZXJlIjoib3ZlciIsInJhaW5ib3ciOiJ3YXkiLCJ1cCI6ImhpZ2giLCJhbmQiOiJ0aGUiLCJkcmVhbXMiOiJ5b3UiLCJkcmVhbWVkIjoib2YifQ.AHNJxSTiDw_bWNXcuh-LtPLvSjJqwDvOOUcmkk7CyZA

Ta je naivčina velika! Sad, pogodimo malo drugačiju krajnju točku istim tvrdnjama:

http -v POST localhost: 8080 / dynamic-builder-compress \ iss = Stormpath hasMotorcycle: = true sub = msilverman the = quick brown = lisica je skočila = preko lijenog = pas \ negdje = preko duge = put gore = visoko i = snovi = sanjali ste = o

Ovaj put dobivamo:

eyJhbGciOiJIUzI1NiIsImNhbGciOiJERUYifQ.eNpEzkESwjAIBdC7sO4JegdXnoC2tIk2oZLEGB3v7s84jjse_AFe5FOikc5ZLRycHQ3kOJ0Untu8C43ZigyUyoRYSH6_iwWOyGWHKd2Kn6_QZFojvOoDupRwyAIq4vDOzwYtugFJg1QnJv-5sY-TVjQqN7gcKJ3f-j8c-6J-baDFhEN_uGn58XtnpfcHAAD__w.3_wc-2skFBbInk0YAQ96yGWwr8r1xVdbHn-uGPTFuFE

62 znaka kraće! Evo koda za metodu koja se koristi za generiranje JWT:

@RequestMapping (value = "/ dynamic-builder-compress", method = POST) javni JwtResponse dynamicBuildercompress (tvrdnje @RequestBody Map) baca UnsupportedEncodingException {String jws = Jwts.builder () .setClaims (zahtjevi) .compressWith (CompressionCodecs.DEFLATEATE). .signWith (SignatureAlgorithm.HS256, secretService.getHS256SecretBytes ()) .compact (); vratiti novi JwtResponse (jws); }

Obavijest u retku 6 navodimo algoritam kompresije koji će se koristiti. To je sve.

Što je s raščlanjivanjem komprimiranih JWT-ova? JJWT knjižnica automatski otkriva kompresiju i koristi isti algoritam za dekompresiju:

GET /parser?jwt=eyJhbGciOiJIUzI1NiIsImNhbGciOiJERUYifQ.eNpEzkESwjAIBdC7sO4JegdXnoC2tIk2oZLEGB3v7s84jjse_AFe5FOikc5ZLRycHQ3kOJ0Untu8C43ZigyUyoRYSH6_iwWOyGWHKd2Kn6_QZFojvOoDupRwyAIq4vDOzwYtugFJg1QnJv-5sY-TVjQqN7gcKJ3f-j8c-6J-baDFhEN_uGn58XtnpfcHAAD__w.3_wc-2skFBbInk0YAQ96yGWwr8r1xVdbHn-uGPTFuFE HTTP / 1.1 Prihvati: * / * ... HTTP / 1.1 200 OK Cache-Control: no-cache, nema -store, max-age = 0, mora ponovno potvrditi vrstu sadržaja: application / json; charset = UTF-8 ... {"potraživanja": {"body": {"i": "the", "brown" : "lisica", "sanjao": "od", "snovi": "ti", "imaMotocikl": istina, "iss": "Oluja", "skočio": "preko", "lijen": "pas" , "rainbow": "way", "negdje": "over", "sub": "msilverman", "the": "quick", "up": "high"}, "header": {"alg" : "HS256", "calg": "DEF"}, "potpis": "3_wc-2skFBbInk0YAQ96yGWwr8r1xVdbHn-uGPTFuFE"}, "status": "USPJEH"}

Primijetite kalg zahtjev u zaglavlju. To je automatski kodirano u JWT i daje parseru nagovještaj koji algoritam koristiti za dekompresiju.

NAPOMENA: Specifikacija JWE podržava kompresiju. U nadolazećem izdanju JJWT biblioteke podržavat ćemo JWE i komprimirane JWE. Nastavit ćemo podržavati kompresiju u drugim vrstama JWT-a, iako nije određeno.

8. Token alati za Java programere

Iako središnji fokus ovog članka nije bio Spring Boot ili Spring Security, pomoću te dvije tehnologije bilo je lako demonstrirati sve značajke o kojima se govori u ovom članku. Trebali biste biti u mogućnosti ugraditi požar u poslužitelju i početi se igrati s različitim krajnjim točkama o kojima smo razgovarali. Samo pogodite:

http // localhost: 8080

Stormpath je također uzbuđen što je u Java zajednicu donio brojne alate za razvojne programere otvorenog koda. To uključuje:

8.1. JJWT (O čemu smo razgovarali)

JJWT je jednostavan alat za programere za stvaranje i provjeru JWT-ova u Javi. Kao i mnoge biblioteke koje Stormpath podržava, JJWT je potpuno besplatan i otvoren izvor (Apache licenca, verzija 2.0), tako da svi mogu vidjeti što radi i kako to radi. Ne ustručavajte se prijaviti bilo kakve probleme, predložiti poboljšanja, pa čak i poslati neki kôd!

8.2. jsonwebtoken.io i java.jsonwebtoken.io

jsonwebtoken.io je razvojni alat koji smo stvorili kako bismo olakšali dekodiranje JWT-ova. Jednostavno zalijepite postojeći JWT u odgovarajuće polje da biste dekodirali njegovo zaglavlje, korisni teret i potpis. jsonwebtoken.io pokreće nJWT, najčišća besplatna i otvorena koda (Apache licenca, verzija 2.0) JWT biblioteka za programere Node.js. Na ovom web mjestu također možete vidjeti kod generiran za razne jezike. Sama web stranica je otvorenog koda i može se naći ovdje.

java.jsonwebtoken.io je posebno za JJWT knjižnicu. Možete izmijeniti zaglavlja i korisni teret u gornjem desnom okviru, vidjeti JWT koji generira JJWT u gornjem lijevom okviru i vidjeti uzorak Java koda graditelja i raščlanjivača u donjim okvirima. Sama web stranica je otvorenog koda i može se naći ovdje.

8.3. JWT inspektor

Novo dijete u bloku, JWT Inspector je Chromeovo proširenje otvorenog koda koje omogućava programerima da pregledaju i ispravljaju JWT-ove izravno u pregledniku. JWT Inspector otkrit će JWT-ove na vašoj web lokaciji (u kolačićima, lokalnoj pohrani / pohrani sesija i zaglavljima) i učiniti ih lako dostupnima putem vaše navigacijske trake i ploče DevTools.

9. JWT ovo dolje!

JWT dodaju određenu inteligenciju običnim žetonima. Sposobnost kriptografskog potpisivanja i provjere, ugradnje vremena isteka i kodiranja ostalih podataka u JWT-ove postavlja pozornicu za istinsko upravljanje sjednicama bez državljanstva. To ima velik utjecaj na sposobnost skaliranja aplikacija.

U Stormpathu, između ostalog, koristimo JWT za OAuth2 tokene, CSRF tokene i tvrdnje između mikrousluga.

Jednom kada počnete koristiti JWT-ove, možda se više nećete vratiti nemim znakovima prošlosti. Imate li pitanja? Udari me na @afitnerd na twitteru.


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