Preplňujte autentifikáciu Java pomocou webových tokenov JSON (JWT)

Chcete sa pripraviť na vytvorenie bezpečnej autentifikácie vo svojej aplikácii Java alebo s ňou bojovať? Nie ste si istí výhodami používania tokenov (a konkrétne webových tokenov JSON) alebo ako by sa mali nasadiť? Som rád, že vám v tomto tutoriále odpoviem na tieto a ďalšie otázky!

Predtým, ako sa ponoríme do webových tokenov JSON (JWT), a knižnice JJWT (vytvorená CTO spoločnosti Stormpath, Les Hazlewood a udržiavaná komunitou prispievateľov), poďme si vysvetliť niekoľko základných informácií.

1. Autentifikácia vs.

Sada protokolov, ktoré aplikácia používa na potvrdenie totožnosti používateľa, je autentifikácia. Aplikácie tradične pretrvávajú prostredníctvom identifikačných súborov cookie. Táto paradigma sa spolieha na ukladanie ID relácií na stranu servera, čo núti vývojárov vytvárať úložiská relácií, ktoré sú buď jedinečné a špecifické pre server, alebo implementované ako úplne samostatná vrstva úložiska relácií.

Autentifikácia pomocou tokenov bola vyvinutá na riešenie problémov ID relácií na strane servera neboli, ani nemohli. Rovnako ako tradičné overovanie, aj používatelia poskytujú overiteľné poverenia, namiesto ID relácie sa im teraz vydáva sada tokenov. Počiatočné poverenia môžu byť štandardný pár používateľské meno / heslo, kľúče API alebo dokonca tokeny z inej služby. (Príkladom toho je funkcia Stormpath API Key Authentication.)

1.1. Prečo tokeny?

Veľmi jednoducho použitie tokenov namiesto ID relácií môže znížiť zaťaženie servera, zjednodušiť správu povolení a poskytnúť lepšie nástroje na podporu distribuovanej alebo cloudovej infraštruktúry. V prípade JWT sa to primárne dosahuje bezstavovou povahou týchto typov tokenov (viac nižšie).

Tokeny ponúkajú širokú škálu aplikácií vrátane: schém ochrany CSRF (Cross Site Request Forgery), interakcií OAuth 2.0, ID relácií a (v súboroch cookie) ako autentifikačné reprezentácie. Vo väčšine prípadov normy neurčujú konkrétny formát tokenov. Tu je príklad typického tokenu Spring Security CSRF vo forme HTML:

Ak sa pokúsite odoslať tento formulár bez správneho tokenu CSRF, dostanete chybovú odpoveď a to je užitočnosť tokenov. Vyššie uvedený príklad je „hlúpy“ token. To znamená, že zo samotného tokenu neexistuje žiadny inherentný význam. To je tiež miesto, kde JWT robia veľký rozdiel.

2. Čo je v JWT?

JWT (vyslovované ako „jots“) sú bezpečné, URL, kódované, kryptograficky podpísané (niekedy šifrované) reťazce, ktoré možno použiť ako tokeny v rôznych aplikáciách. Tu je príklad použitia JWT ako tokenu CSRF:

V tomto prípade vidíte, že token je oveľa dlhší ako v našom predchádzajúcom príklade. Rovnako ako sme videli predtým, ak sa formulár odošle bez tokenu, dostanete chybovú odpoveď.

Prečo teda JWT?

Vyššie uvedený token je kryptograficky podpísaný, a preto ho možno overiť, aby poskytol dôkaz, že k nemu nedošlo. JWT sú tiež kódované s rôznymi ďalšími informáciami.

Pozrime sa na anatómiu JWT, aby sme lepšie pochopili, ako z toho vyžmýkame celú túto dobrotu. Možno ste si všimli, že existujú tri odlišné časti oddelené bodkami (.):

HlavičkaeyJhbGciOiJIUzI1NiJ9
Užitočné zaťaženieeyJqdGkiOiJlNjc4ZjIzMzQ3ZTM0MTBkYjdlNjg3Njc4MjNiMmQ3MCIsImlhdC

I6MTQ2NjYzMzMxNywibmJmIjoxNDY2NjMzMzE3LCJleHAiOjE0NjY2MzY5MTd9

Podpisrgx_o8VQGuDa2AqCHSgVOD5G68Ld_YYM7N7THmvLIKc

Každá sekcia je kódovaná pomocou adresy URL base64. To zaisťuje, že je možné ho bezpečne použiť v adrese URL (viac o tom neskôr). Pozrime sa podrobnejšie na každú časť zvlášť.

2.1. Hlavička

Ak použijete base64 na dekódovanie hlavičky, získate nasledujúci reťazec JSON:

{"alg": "HS256"}

To ukazuje, že JWT bol podpísaný s HMAC pomocou SHA-256.

2.2. Užitočné zaťaženie

Ak dekódujete užitočné zaťaženie, získate nasledujúci reťazec JSON (formátovaný kvôli prehľadnosti):

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

Ako vidíte, v rámci užitočného zaťaženia je množstvo kľúčov s hodnotami. Tieto kľúče sa nazývajú „nároky“ a špecifikácia JWT ich má sedem špecifikovaných ako „registrované“ nároky. Oni sú:

issVydavateľ
podčPredmet
audPublikum
expExpirácia
nbfNie predtým
iatVydané o
jtiJWT ID

Pri vytváraní JWT môžete uviesť akékoľvek vlastné nároky, ktoré si prajete. Zoznam vyššie predstavuje iba deklarácie, ktoré sú vyhradené pre použitý kľúč aj pre očakávaný typ. Náš CSRF má JWT ID, čas „Vydaný v“, „Nie skôr“ a Čas vypršania platnosti. Čas vypršania platnosti je presne minútu po vydaní v danom čase.

2.3. Podpis

Nakoniec sa podpisová sekcia vytvorí tak, že sa záhlavie a užitočné zaťaženie spoja (s. Medzi) a odovzdajú sa cez zadaný algoritmus (v tomto prípade HMAC pomocou SHA-256) spolu so známym tajomstvom. Všimnite si, že tajomstvo je vždy bajtové pole a mala by mať dĺžku, ktorá dáva zmysel použitému algoritmu. Ďalej používam náhodný reťazec zakódovaný v base64 (kvôli čitateľnosti), ktorý je prevedený na bajtové pole.

V pseudokode to vyzerá takto:

computeHMACSHA256 (header + "." + užitočné zaťaženie, base64DecodeToByteArray ("4pE8z3PBoHjnV1AhvGk + e8h2p + ShZpOnpr8cwHmMh1w ="))

Pokiaľ poznáte tajomstvo, môžete podpis vygenerovať sami a porovnať svoj výsledok s časťou podpisu JWT a overiť, či s ním nebolo manipulované. Technicky sa JWT, ktoré bolo kryptograficky podpísané, nazýva JWS. JWT môžu byť tiež šifrované a potom by sa nazývali JWE. (V praxi sa termín JWT používa na označenie JWE a JWS.)

To nás vracia k výhodám používania JWT ako nášho tokenu CSRF. Podpis môžeme overiť a na potvrdenie jeho platnosti môžeme použiť informácie zakódované v JWT. Takže nielenže sa musí reťazcové zastúpenie JWT zhodovať s tým, čo je uložené na strane servera, môžeme zabezpečiť, aby jeho platnosť nevypršala jednoducho kontrolou exp nárok. Toto ušetrí serveru zachovanie ďalšieho stavu.

No, pokryli sme tu veľa pôdy. Poďme sa ponoriť do nejakého kódu!

3. Pripravte výukový program JJWT

JJWT (//github.com/jwtk/jjwt) je knižnica Java, ktorá poskytuje komplexné vytváranie a overovanie webových tokenov JSON. Navždy bezplatný a otvorený zdroj (licencia Apache, verzia 2.0) bol navrhnutý s rozhraním zameraným na staviteľa, ktoré ukrýva väčšinu svojej zložitosti.

Primárne operácie pri používaní JJWT zahŕňajú vytváranie a syntaktickú analýzu JWT. Ďalej sa pozrieme na tieto operácie, potom sa pozrieme na niektoré rozšírené funkcie JJWT a nakoniec uvidíme JWT v akcii ako tokeny CSRF v aplikácii Spring Security, Spring Boot.

Kód uvedený v nasledujúcich častiach nájdete tu. Poznámka: Projekt používa Spring Boot od začiatku ako ľahkú interakciu s API, ktoré vystavuje.

Pri zostavovaní projektu postupujte takto:

git clone //github.com/eugenp/tutorials.git cd tutoriály / jjwt mvn clean install

Jednou z veľkých vecí Spring Spring je to, aké ľahké je spustiť aplikáciu. Ak chcete spustiť aplikáciu JJWT Fun, postupujte takto:

java -jar target / *. jar 

V tejto ukážkovej aplikácii je vystavených desať koncových bodov (na interakciu s aplikáciou používam httpie. Nájdete ich tu.)

http localhost: 8080
Dostupné príkazy (predpokladá httpie - //github.com/jkbrzt/httpie): http // localhost: 8080 / Táto správa o použití http // localhost: 8080 / static-builder zostavuje JWT z pevne zakódovaných deklarácií http POST // localhost: 8080 / dynamic-builder-general claims-1 = value-1 ... [claim-n = value-n] build JWT from passed in claims (using general claims map) http POST // localhost: 8080 / dynamic-builder-specific claim -1 = hodnota-1 ... [deklarácia-n = hodnota-n] zostavenie JWT z odovzdaných deklarácií (pomocou špecifických metód deklarácií) http POST // localhost: 8080 / dynamic-builder-compress deklarácia-1 = hodnota-1 ... [claim-n = value-n] build DEFLATE komprimovaný JWT z odovzdaného v nárokoch http // localhost: 8080 / parser? jwt = analyzovaný predaný v JWT http // localhost: 8080 / parser-vynutit? jwt = analyzovaný prešiel v JWT presadzovanie „iss“ registrovaného nároku a „hasMotorcycle“ vlastného nároku http // localhost: 8080 / get-secrets Zobraziť aktuálne používané podpisové kľúče. http // localhost: 8080 / refresh-secrets Vytvorte nové podpisové kľúče a ukážte ich. http POST // localhost: 8080 / set-secrets HS256 = kódovaná hodnota base64 HS384 = kódovaná hodnota base64 HS512 = kódovaná hodnota base64 Hodnota tajne nastavená na použitie v aplikácii je explicitne nastavená.

V nasledujúcich častiach preskúmame každý z týchto koncových bodov a kód JJWT obsiahnutý v obslužných programoch.

4. Budovanie JWT pomocou JJWT

Vzhľadom na plynulé rozhranie JJWT je vytvorenie JWT v zásade trojstupňový proces:

  1. Definícia interných nárokov tokenu, napríklad Emitent, Predmet, Vypršanie platnosti a ID.
  2. Kryptografické podpísanie JWT (čo z neho robí JWS).
  3. Zhutnenie JWT na reťazec bezpečný pre URL podľa pravidiel JWT Compact Serialization.

Konečným JWT bude trojdielny reťazec kódovaný base64, podpísaný špecifikovaným podpisovým algoritmom a pomocou poskytnutého kľúča. Po tomto bode je token pripravený na zdieľanie s druhou stranou.

Tu je príklad JJWT v akcii:

String jws = Jwts.builder () .setIssuer ("Stormpath") .setSubject ("msilverman") .claim ("name", "Micah Silverman") .claim ("scope", "admins") // Pi 24. júna 2016 15:33:42 GMT-0400 (EDT). SetIssuedAt (Date.from (Instant.ofEpochSecond (1466796822L))) // so 24. júna 2116 15:33:42 GMT-0400 (EDT). SetExpiration (Date.from (Instant.ofEpochSecond (4622470422L))) .signWith (SignatureAlgorithm.HS256, TextCodec.BASE64.decode ("Yn2kjibddFAWtnPJ2AFlL8WXmohJMCvigQggaEypa5E =")) .compact

Je to veľmi podobné ako v kóde StaticJWTController.fixedBuilder metóda kódového projektu.

V tomto okamihu stojí za to hovoriť o niekoľkých anti-vzoroch týkajúcich sa JWT a podpisovania. Ak ste už niekedy videli príklady JWT, pravdepodobne ste sa stretli s jedným z týchto scenárov podpisovania vzorov:

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

Niektorý z HS algoritmy podpisu typu zaberajú bajtové pole. Pre ľudí je pohodlné čítať, vziať reťazec a previesť ho na bajtové pole.

Vyššie uvedený vzor 1 to demonštruje. To je problematické, pretože tajomstvo je oslabené tým, že je také krátke, a nejde o bajtové pole v pôvodnej podobe. Aby bola čitateľná, môžeme base64 kódovať bajtové pole.

Avšak vyššie uvedený vzor 2 prevezme reťazec zakódovaný v base64 a prevedie ho priamo na bajtové pole. Čo by sa malo urobiť, je dekódovať reťazec base64 späť do pôvodného bajtového poľa.

Toto ukazuje číslo 3 vyššie. Prečo je teda tento aj anti-vzor? V tomto prípade je to jemný dôvod. Všimnite si, že podpisový algoritmus je HS512. Bajtové pole nie je maximálna dĺžka HS512 môže podporovať, čo z neho robí slabšie tajomstvo, ako je možné pre tento algoritmus.

Vzorový kód obsahuje triedu s názvom Tajná služba ktorý zaisťuje, že pre daný algoritmus sú použité tajomstvá správnej sily. V čase spustenia aplikácie sa pre každý z algoritmov HS vytvorí nová sada tajomstiev. Existujú koncové body na obnovenie tajomstiev, ako aj na výslovné nastavenie tajomstiev.

Ak máte projekt spustený podľa vyššie uvedeného popisu, vykonajte nasledujúce kroky, aby sa nižšie uvedené príklady JWT zhodovali s odpoveďami z vášho projektu.

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

Teraz môžete stlačiť / static-builder koncový bod:

http // localhost: 8080 / static-builder

Takto sa vytvorí JWT, ktorý vyzerá takto:

eyJhbGciOiJIUzI1NiJ9. eyJpc3MiOiJTdG9ybXBhdGgiLCJzdWIiOiJtc2lsdmVybWFuIiwibmFtZSI6Ik1pY2FoIFNpbHZlcm1hbiIsInNjb3BlIjoiYWRtaW5zIi02N2J2J22JJ22J22J22J22J22J22 kP0i_RvTAmI8mgpIkDFhRX3XthSdP-eqqFKGcU92ZIQ

Teraz stlačte:

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

Odpoveď má všetky nároky, ktoré sme zahrnuli, keď sme vytvorili JWT.

HTTP / 1.1 200 OK Content-Type: application / json; charset = UTF-8 ... {"jws": {"body": {"exp": 4622470422, "iat": 1466796822, "iss": "Stormpath "," name ":" Micah Silverman "," scope ":" admins "," sub ":" msilverman "}," header ": {" alg ":" HS256 "}," podpis ":" kP0i_RvTAmI8mgpIkDFhRX3XthSdP-eqqFKGcU92ZIQ "}," status ":" ÚSPECH "}

Toto je operácia syntaktickej analýzy, ktorej sa budeme venovať v nasledujúcej časti.

Teraz poďme na koncový bod, ktorý berie nároky ako parametre a vytvorí pre nás vlastný JWT.

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

Poznámka: Medzi tým je jemný rozdiel hasMotocykel pohľadávka a ďalšie pohľadávky. httpie predpokladá, že parametre JSON sú predvolene reťazce. Ak chcete odoslať surový JSON pomocou programu httpie, použijete := forma skôr ako =. Bez toho by sa to podrobilo „HasMotorcycle“: „true“, čo nie je to, čo chceme.

Tu je výstup:

POST / dynamic-builder-general HTTP / 1.1 Accept: application / json ... {"hasMotorcycle": true, "iss": "Stormpath", "sub": "msilverman"} HTTP / 1.1 200 OK Content-Type: application / json; charset = UTF-8 ... { "JWT": "eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJTdG9ybXBhdGgiLCJzdWIiOiJtc2lsdmVybWFuIiwiaGFzTW90b3JjeWNsZSI6dHJ1ZX0.OnyDs-zoL3-rw1GaSl_KzZzHK9GoiNocu-YwZ_nQNZU", "stav": "úspech"} 

Pozrime sa na kód, ktorý podporuje tento koncový bod:

@RequestMapping (value = "/ dynamic-builder-general", method = POST) public JwtResponse dynamicBuilderGeneric (@RequestBody Map claims) hodí UnsupportedEncodingException {String jws = Jwts.builder () .setClaims (claims) .signWith (SignatureAlgorithm, SignatureAlgorithm, secretService.getHS256SecretBytes ()) .compact (); vrátiť nové JwtResponse (jws); }

Riadok 2 zaisťuje, že prichádzajúci súbor JSON sa automaticky prevedie na mapu Java Map, čo je pre JJWT veľmi užitočné, pretože metóda na riadku 5 jednoducho vezme túto mapu a nastaví všetky nároky naraz.

Aj keď je tento kód stručný, potrebujeme niečo konkrétnejšie, aby sme zaistili platnosť deklarovaných nárokov. Pomocou .setClaims (nároky na mape) Táto metóda je užitočná, keď už viete, že nároky uvedené v mape sú platné. To je miesto, kde typová bezpečnosť Java prichádza do knižnice JJWT.

Pre každú z registrovaných sťažností definovaných v špecifikácii JWT existuje v JJWT zodpovedajúca metóda Java, ktorá má typ správny.

Poďme zasiahnuť ďalší koncový bod v našom príklade a uvidíme, čo sa stane:

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

Upozorňujeme, že pre nárok „sub“ sme vložili celé číslo 5. Tu je výstup:

Špecifický pre POST / dynamický staviteľ HTTP / 1.1 Prijať: application / json ... {"hasMotorcycle": true, "iss": "Stormpath", "sub": 5} Nesprávne pripojenie HTTP / 1.1 400: zatvorte obsah- Typ: application / json; charset = UTF-8 ... {"exceptionType": "java.lang.ClassCastException", "message": "java.lang.Integer nemožno preniesť na java.lang.String", "status ": "CHYBA" }

Teraz dostávame chybové hlásenie, pretože kód vynucuje typ registrovaných sťažností. V tomto prípade, podč musí byť reťazec. Tu je kód, ktorý podporuje tento koncový bod:

@RequestMapping (value = "/ dynamic-builder-specific", method = POST) public JwtResponse dynamicBuilderSpecific (@RequestBody Map claims) hodí UnsupportedEncodingException {JwtBuilder builder = Jwts.builder (); claimss.forEach ((key, value) -> {switch (key) {case "iss": builder.setIssuer ((String) value); break; case "sub": builder.setSubject ((String) value); break ; case "aud": builder.setAudience ((String) value); break; case "exp": builder.setExpiration (Date.from (Instant.ofEpochSecond (Long.parseLong (value.toString ())))); break ; case "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 (key, value);}}); builder.signWith (SignatureAlgorithm.HS256, secretService.getHS256SecretBytes ()); vrátiť nový JwtResponse (builder.compact ()); }

Rovnako ako predtým metóda akceptuje a Mapa ako parameter. Tentokrát však voláme špecifickú metódu pre každý z registrovaných nárokov, ktorá vynucuje typ.

Ďalším vylepšením je spresnenie chybovej správy. Momentálne vieme iba to, že jedno z našich tvrdení nie je správneho typu. Nevieme, ktorá žiadosť bola omylom, ani to, čo by malo byť. Tu je metóda, ktorá nám poskytne konkrétnejšie chybové hlásenie. Zaoberá sa tiež chybou v aktuálnom kóde.

private void ensureType (String registeredClaim, Object value, Class expectType) {boolean isCorrectType = expectType.isInstance (value) || expectType == Long.class && value instanceof Integer; if (! isCorrectType) {String msg = "Očakávaný typ:" + expectType.getCanonicalName () + "pre registrovanú požiadavku: '" + registeredClaim + "', ale získal hodnotu:" + value + "typu:" + value. getClass (). getCanonicalName (); hodiť novú JwtException (msg); }}

Riadok 3 skontroluje, či je odovzdaná hodnota očakávaného typu. Ak nie, a JwtException je vyvolaná s konkrétnou chybou. Poďme sa na to pozrieť v akcii uskutočnením rovnakého hovoru, ktorý sme uskutočnili predtým:

http -v POST localhost: 8080 / dynamic-builder-specific iss = Stormpath sub: = 5 hasMotorcycle: = true
POST / špecifický pre HTTP / builder HTTP / 1.1 Prijať: application / json ...User-Agent: HTTPie / 0.9.3 {"hasMotorcycle": true, "iss": "Stormpath", "sub": 5} Nesprávne pripojenie HTTP / 1.1 400: zatvoriť Content-Type: application / json; charset = UTF -8 ... {"exceptionType": "io.jsonwebtoken.JwtException", "message": "Očakávaný typ: java.lang. Reťazec pre registrovaný nárok: 'sub', ale dostal hodnotu: 5 typu: java.lang .Integer "," status ":" CHYBA "}

Teraz máme veľmi konkrétnu chybovú správu, ktorá nám hovorí, že: podč nárok je omylom.

Vráťme sa k tejto chybe v našom kóde. Toto vydanie nemá nič spoločné s knižnicou JJWT. Problém je v tom, že mapovač objektov JSON na Java zabudovaný do Spring Boot je príliš inteligentný pre naše dobro.

Ak existuje metóda, ktorá prijíma objekt Java, mapovač JSON automaticky prevedie odovzdaný počet v počte, ktorý je menší alebo rovný 2 147 483 647, na jazyk Java Celé číslo. Rovnako tak automaticky prevedie odovzdaný počet, ktorý je väčší ako 2 147 483 647, do jazyka Java Dlhé. Pre iat, nbfa exp deklarácie JWT, chceme, aby náš test zaistenia typu prešiel, či je mapovaný objekt celé číslo alebo dlhý. Preto máme dodatočnú klauzulu pri určovaní, či je odovzdaná hodnota správneho typu:

 boolean isCorrectType = expectType.isInstance (hodnota) || expectType == Long.class && value instanceof Integer;

Ak očakávame Long, ale hodnota je inštanciou Integer, stále hovoríme, že je to správny typ. S porozumením toho, čo sa deje s touto validáciou, ju teraz môžeme integrovať do našej dynamicBuilderSpecific metóda:

@RequestMapping (value = "/ dynamic-builder-specific", method = POST) public JwtResponse dynamicBuilderSpecific (@RequestBody Map claims) hodí UnsupportedEncodingException {JwtBuilder builder = Jwts.builder (); claimss.forEach ((key, value) -> {switch (key) {case "iss": ensureType (key, value, String.class); builder.setIssuer ((String) value); break; case "sub": sureType (key, value, String.class); builder.setSubject ((String) value); break; case "aud": ensureType (key, value, String.class); builder.setAudience ((String) value); break ; case "exp": ensureType (key, value, Long.class); builder.setExpiration (Date.from (Instant.ofEpochSecond (Long.parseLong (value.toString ())))); break; case "nbf": sureType (key, value, Long.class); builder.setNotBefore (Date.from (Instant.ofEpochSecond (Long.parseLong (value.toString ())))); break; case "iat": secureType (kľúč, hodnota, Long.class); builder.setIssuedAt (Date.from (Instant.ofEpochSecond (Long.parseLong (value.toString ())))); break; case "jti": ensureType (key, value, String.class); builder .setId ((String) value); break; default: builder.claim (key, value);}}); builder.signWith (SignatureAlgorithm.HS256, secretService.getHS256SecretBytes ()); vrátiť nový JwtResponse (builder.compact ()); }

Poznámka: Vo všetkých príkladoch kódu v tejto časti sú súbory JWT podpísané s HMAC pomocou algoritmu SHA-256. Z dôvodu zjednodušenia príkladov. Knižnica JJWT podporuje 12 rôznych podpisových algoritmov, ktoré môžete využiť vo svojom vlastnom kóde.

5. Analýza JWT pomocou JJWT

Už sme videli, že náš príklad kódu má koncový bod na analýzu JWT. Dosiahnutie tohto koncového bodu:

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

vytvára túto odpoveď:

HTTP / 1.1 200 OK Content-Type: application / json; charset = UTF-8 ... {"claims": {"body": {"exp": 4622470422, "iat": 1466796822, "iss": "Stormpath "," name ":" Micah Silverman "," scope ":" admins "," sub ":" msilverman "}," header ": {" alg ":" HS256 "}," podpis ":" kP0i_RvTAmI8mgpIkDFhRX3XthSdP-eqqFKGcU92ZIQ "}," status ":" ÚSPECH "}

The parser metóda StaticJWTController trieda vyzerá takto:

@RequestMapping (value = "/ parser", method = GET) verejný analyzátor JwtResponse (@RequestParam reťazec jwt) hodí UnsupportedEncodingException {Jws jws = Jwts.parser () .setSigningKeyResolver (secretService.getSigningKeyResol) vrátiť nové JwtResponse (jws); }

Riadok 4 naznačuje, že očakávame, že prichádzajúci reťazec bude podpísaný JWT (a JWS). Používame rovnaké tajomstvo, aké sa použilo na podpísanie JWT pri jeho analýze. Riadok 5 analyzuje pohľadávky zo strany JWT. Interne overuje podpis a v prípade neplatného podpisu spôsobí výnimku.

Všimnite si, že v tomto prípade odovzdávame a SigningKeyResolver skôr ako samotný kľúč. Toto je jeden z najsilnejších aspektov JJWT. Hlavička JWT označuje algoritmus použitý na jeho podpísanie. Musíme však JWT overiť skôr, ako mu uveríme. Zdalo by sa to byť úlovkom 22. Pozrime sa na SecretService.getSigningKeyResolver metóda:

private SigningKeyResolver signingKeyResolver = nový SigningKeyResolverAdapter () {@Override verejný bajt [] resolveSigningKeyBytes (hlavička JwsHeader, reklamácie) {return TextCodec.BASE64.decode (secrets.get (header.getAlgorithm ())); }};

Využívanie prístupu k internetu JwsHeader, Môžem skontrolovať algoritmus a vrátiť správne bajtové pole pre tajomstvo, ktoré bolo použité na podpísanie JWT. Teraz JJWT overí, že s JWT nebolo manipulované použitie tohto bajtového poľa ako kľúča.

Ak odstránim posledný znak odovzdaného v JWT (ktorý je súčasťou podpisu), je to odpoveď:

Nesprávne pripojenie HTTP / 1.1 400: zavrieť Typ obsahu: application / json; charset = UTF-8 Dátum: pondelok, 27. júna 2016 13:19:08 GMT server: Apache-Coyote / 1.1 Transfer-Encoding: chunked {"exceptionType ":" io.jsonwebtoken.SignatureException "," message ":" Podpis JWT sa nezhoduje s lokálne vypočítaným podpisom. Platnosť JWT nie je možné tvrdiť a nemalo by sa jej dôverovať. "," status ":" CHYBA "}

6. JWT v praxi: Jarné bezpečnostné CSRF tokeny

Aj keď zameranie tohto príspevku nie je na jarné zabezpečenie, budeme sa tu trochu zaoberať, aby sme predstavili použitie knižnice JJWT v reálnom svete.

Cross Site Request Forgery je chyba zabezpečenia, pomocou ktorej vás škodlivá webová stránka podvedie k odoslaniu žiadosti na web, ku ktorému ste si vytvorili dôveru. Jedným z bežných nápravných opatrení je implementácia vzoru tokenu synchronizátora. Tento prístup vloží token do webového formulára a aplikačný server skontroluje prichádzajúci token oproti svojmu úložisku, aby potvrdil, že je správny. Ak token chýba alebo je neplatný, server odpovie chybou.

Spring Security má zabudovaný vzor synchronizačného tokenu. Ešte lepšie je, že ak používate šablóny Spring Boot a Thymeleaf, synchronizačný token sa vloží automaticky za vás.

V predvolenom nastavení je token, ktorý používa Spring Security, „hlúpy“ token. Je to iba séria písmen a čísel. Tento prístup je v poriadku a funguje. V tejto časti vylepšujeme základné funkcie používaním JWT ako tokenu. Okrem overenia, či je predložený token ten, ktorý sa očakáva, overujeme JWT, aby sme ďalej preukázali, že s tokenom nebolo manipulované, a aby sme zaistili jeho platnosť.

Na začiatok ideme konfigurovať Spring Security pomocou konfigurácie Java. Štandardne všetky cesty vyžadujú autentifikáciu a všetky koncové body POST vyžadujú tokeny CSRF. Trochu si to oddýchneme, aby to, čo sme doteraz vybudovali, stále fungovalo.

@Configuration verejná trieda WebSecurityConfig rozširuje WebSecurityConfigurerAdapter {private String [] ignoreCsrfAntMatchers = {"/ dynamic-builder-compress", "/ dynamic-builder-general", "/ dynamic-builder-specific", "/ set-secrets"}; @Override protected void configure (HttpSecurity http) vyvolá výnimku {http .csrf () .ignoringAntMatchers (ignoreCsrfAntMatchers). A (). AuthorizeRequests () .antMatchers ("/ **") .permitAll (); }}

Robíme tu dve veci. Najprv hovoríme, že tokeny CSRF sú nie požadované pri odosielaní do našich koncových bodov REST API (riadok 15). Po druhé, hovoríme, že by mal byť povolený neautentizovaný prístup pre všetky cesty (riadky 17 - 18).

Potvrdíme, že jarná bezpečnosť funguje tak, ako očakávame. Spustite aplikáciu a stlačte túto adresu URL v prehliadači:

// localhost: 8080 / jwt-csrf-form

Tu je šablóna Thymeleaf pre toto zobrazenie:

Toto je veľmi základný formulár, ktorý sa po odoslaní zverejní v rovnakom koncovom bode. Všimnite si, že vo formulári nie je výslovný odkaz na tokeny CSRF. Ak si pozriete zdroj, uvidíte niečo ako:

Toto je všetko potvrdenie, ktoré potrebujete vedieť, že Spring Security funguje a že šablóny Thymeleaf automaticky vkladajú token CSRF.

Ak chcete z hodnoty urobiť JWT, povolíme zvyk CsrfTokenRepository. Tu je ukážka toho, ako sa mení naša konfigurácia jarného zabezpečenia:

@Configuration verejná trieda WebSecurityConfig rozširuje WebSecurityConfigurerAdapter {@Autowired CsrfTokenRepository jwtCsrfTokenRepository; @Override protected void configure (HttpSecurity http) vyvolá výnimku {http .csrf () .csrfTokenRepository (jwtCsrfTokenRepository) .ignoringAntMatchers (ignoreCsrfAntMatchers). A (). AuthorizeRequests () .antMatchers ("/ **" }}

Aby sme to mohli prepojiť, potrebujeme konfiguráciu, ktorá sprístupní fazuľu, ktorá vráti úložisko vlastných tokenov. Tu je konfigurácia:

@ Konfigurácia verejná trieda CSRFConfig {@Autowired SecretService secretService; @Bean @ConditionalOnMissingBean public CsrfTokenRepository jwtCsrfTokenRepository () {návrat nového JWTCsrfTokenRepository (secretService.getHS256SecretBytes ()); }}

A tu je naše vlastné úložisko (dôležité bity):

verejná trieda JWTCsrfTokenRepository implementuje CsrfTokenRepository {súkromná statická konečná hodnota Logger log = LoggerFactory.getLogger (JWTCsrfTokenRepository.class); súkromný bajt [] tajný; public JWTCsrfTokenRepository (byte [] tajomstvo) {this.secret = tajomstvo; } @Override public CsrfToken generateToken (požiadavka HttpServletRequest) {String id = UUID.randomUUID (). ToString (). Replace ("-", ""); Dátum teraz = nový Dátum (); Dátum exp = nový dátum (System.currentTimeMillis () + (1 000 * 30)); // 30 sekúnd Reťazcový token; skúste {token = Jwts.builder () .setId (id) .setIssuedAt (teraz) .setNotBefore (teraz) .setExpiration (exp) .signWith (SignatureAlgorithm.HS256, tajný) .compact (); } catch (UnsupportedEncodingException e) {log.error ("Nemôžem vytvoriť CSRf JWT: {}", e.getMessage (), e); token = id; } návrat new DefaultCsrfToken ("X-CSRF-TOKEN", "_csrf", token); } @Override public void saveToken (token CsrfToken, požiadavka HttpServletRequest, odpoveď HttpServletResponse) {...} @Override public CsrfToken loadToken (požiadavka HttpServletRequest) {...}}

The generateToken metóda vytvorí JWT, ktorého platnosť vyprší 30 sekúnd po jeho vytvorení. Pomocou tohto vodovodného potrubia môžeme znova spustiť aplikáciu a pozrieť sa na zdroj / jwt-csrf-form.

Teraz skryté pole vyzerá takto:

Huzzah! Náš token CSRF je teraz JWT. To nebolo príliš ťažké.

Toto je však len polovica hádanky. V predvolenom nastavení Spring Security jednoducho uloží token CSRF a potvrdí, že token odoslaný vo webovom formulári sa zhoduje s tokenom, ktorý je uložený. Chceme rozšíriť funkčnosť o validáciu JWT a zabezpečiť, aby jej platnosť nevypršala. Za týmto účelom pridáme filter. Takto vyzerá naša konfigurácia jarného zabezpečenia:

@ Konfigurácia verejnej triedy WebSecurityConfig rozširuje WebSecurityConfigurerAdapter {... @Override chránená neplatná konfigurácia (HttpSecurity http) vyvolá výnimku {http. AddFilterAfter (nový JwtCsrfValidatorFilter (), CsrfFilter.class) .csrfrc) . a (). authorizeRequests () .antMatchers ("/ **") .permitAll (); } ...}

Na riadok 9 sme pridali filter a umiestňujeme ho do reťazca filtra po predvolenom nastavení CsrfFilter. Takže v čase zasiahnutia nášho filtra bude token JWT (ako celok) už potvrdený ako správna hodnota uložená Spring Security.

Tu je JwtCsrfValidatorFilter (je to súkromné, pretože je to vnútorná trieda našej konfigurácie Spring Security):

súkromná trieda JwtCsrfValidatorFilter rozširuje OncePerRequestFilter {@Override chránený void doFilterInternal (požiadavka HttpServletRequest, odpoveď HttpServletResponse, FilterChain filterChain) vyvolá ServletException, IOException {// POZNÁMKA: skutočná implementácia by nemala mať request.getAttribute ("_ csrf"); if (// záleží len na tom, či je to POST "POST" .equals (request.getMethod ()) && // ignorovať, ak je cesta žiadosti v našom zozname Arrays.binarySearch (ignoreCsrfAntMatchers, request.getServletPath ()) <0 && / / uistite sa, že máme token token! = null) {// CsrfFilter už zaistil zhodu tokenu. // Tu sa uistíme, že jeho platnosť nevypršala. Try {Jwts.parser () .setSigningKey (secret.getBytes ("UTF-8")) .parseClaimsJws (token.getToken ()); } catch (JwtException e) {// s najväčšou pravdepodobnosťou ExpiredJwtException, ale toto zvládne akýkoľvek request.setAttribute ("výnimka", e); response.setStatus (HttpServletResponse.SC_BAD_REQUEST); RequestDispatcher dispečer = request.getRequestDispatcher ("expired-jwt"); dispečer.predal (požiadavka, odpoveď); }} filterChain.doFilter (požiadavka, odpoveď); }}

Pozrite sa na riadok 23. Analyzujeme JWT ako predtým. V takom prípade, ak je vyvolaná výnimka, požiadavka sa preposiela na expirované-jwt šablóna. Ak JWT potvrdí platnosť, potom spracovanie pokračuje ako obvykle.

Týmto sa uzavrie slučka pri prepísaní predvoleného správania tokenu Spring Security CSRF s úložiskom a validátorom tokenov JWT.

Ak aplikáciu spustíte, prejdite na / jwt-csrf-form, počkajte niečo viac ako 30 sekúnd a kliknite na tlačidlo, uvidíte niečo ako toto:

7. Rozšírené funkcie JJWT

Našu cestu JJWT ukončíme slovom o niektorých funkciách, ktoré presahujú špecifikáciu.

7.1. Vymáhať nároky

V rámci procesu syntézy vám JJWT umožňuje určiť požadované nároky a hodnoty, ktoré by tieto nároky mali mať. Je to veľmi užitočné, ak vo vašich JWT existujú určité informácie, ktoré musia byť prítomné, aby ste ich mohli považovať za platné. Vyhýba sa mnohým rozvetveniam logiky pri manuálnom overovaní nárokov. Tu slúži metóda, ktorá slúži / syntaktický analyzátor koncový bod nášho ukážkového projektu.

@RequestMapping (value = "/ parser-enforce", method = GET) public JwtResponse parserEnforce (@RequestParam String jwt) vyvolá UnsupportedEncodingException {Jws jws = Jwts.parser () .requireIssuer ("Stormpath") .require ") true) .setSigningKeyResolver (secretService.getSigningKeyResolver ()) .parseClaimsJws (jwt); vrátiť nové JwtResponse (jws); }

Riadky 5 a 6 zobrazujú syntax registrovaných nárokov aj vlastných nárokov. V tomto príklade bude JWT považované za neplatné, ak nárok iss nie je prítomný alebo nemá hodnotu: Stormpath. Bude tiež neplatné, ak vlastná požiadavka hasMotorcycle nie je prítomná alebo nemá hodnotu: true.

Najprv si vytvorme JWT, ktoré nasleduje šťastnú cestu:

http -v POST localhost: 8080 / dynamic-builder-specific \ iss = Stormpath hasMotorcycle: = true sub = msilverman
Špecifický pre POST / dynamický staviteľ HTTP / 1.1 Prijať: application / json ... {"hasMotorcycle": true, "iss": "Stormpath", "sub": "msilverman"} HTTP / 1.1 200 OK Cache-Control: no-cache, no-store, max-age = 0, must-revalidate Content-Type: application / json; charset = UTF-8 ... { "JWT": "status" eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJTdG9ybXBhdGgiLCJoYXNNb3RvcmN5Y2xlIjp0cnVlLCJzdWIiOiJtc2lsdmVybWFuIn0.qrH-U6TLSVlHkZdYuqPRDtgKNr1RilFYQJtJbcgwhR0 " ": "ÚSPECH" }

Poďme si teda overiť, že JWT:

http -v localhost: 8080 / parser-vynucovat? jwt = eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJTdG9ybXBhdGgiLCJoYXNNb3RvcmN5Y2xlIjp0cnVlLCJzdWIiOtj1QVIQ1QVQ1QQV1QV1QV1QV1
? GET / parser-presadiť JWT = http -v localhost: 8080 / parser-presadiť JWT = eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJTdG9ybXBhdGgiLCJoYXNNb3RvcmN5Y2xlIjp0cnVlLCJzdWIiOiJtc2lsdmVybWFuIn0.qrH-U6TLSVlHkZdYuqPRDtgKNr1RilFYQJtJbcgwhR0 HTTP / 1.1 Accept: * / * ... HTTP / 1.1 200 OK Cache-Control: no- cache, no-store, max-age = 0, must-revalidate Content-Type: application / json; charset = UTF-8 ... {"jws": {"body": {"hasMotorcycle": true, "iss ":" Stormpath "," sub ":" msilverman "}," hlavička ": {" alg ":" HS256 "}," podpis ":" qrH-U6TLSVlHkZdYuqPRDtgKNr1RilFYQJtJbcgwhR0 "}," status ":" ÚSPECH "}

Zatiaľ je všetko dobré. Teraz však tentokrát vynechajme motocykel hasMotorcycle:

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

Tentokrát, ak sa pokúsime overiť JWT:

http -v localhost: 8080 / parser-vynucovat? jwt = eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJTdG9ybXBhdGgiLCJzdWIiOiJtc2lsdmVybWFuIn0.YMONlFM1tNgttUYccd9gg9dk99

dostaneme:

? GET / parser-presadiť JWT = http -v localhost: 8080 / parser-presadiť JWT = eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJTdG9ybXBhdGgiLCJzdWIiOiJtc2lsdmVybWFuIn0.YMONlFM1tNgttUYukDRsi9gKIocxdGAOLaJBymaQAWc HTTP / 1.1 Accept: * / * ... HTTP / 1.1 400 Bad Request Cache-Control: no-cache , no-store, max-age = 0, must-revalidate Pripojenie: zavrieť Typ obsahu: application / json; charset = UTF-8 ... {"exceptionType": "io.jsonwebtoken.MissingClaimException", "message": „Očakávané tvrdenie hasMotorcycle je: pravda, ale nebolo uvedené v nárokoch JWT.“, „Status“: „CHYBA“}

To naznačuje, že náš nárok hasMotorcycle sa očakával, ale chýbal.

Urobme ešte jeden príklad:

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

Tentokrát je požadovaný nárok k dispozícii, ale má nesprávnu hodnotu. Pozrime sa na výstup z:

http -v localhost: 8080 / parser-vynucovat? jwt = eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJTdG9ybXBhdGgiLCJoYXNNb3RvcmN5Y2xlIjpmYWxzZSwic3ViIjoibxN1B1H8
GET / parser-presadiť JWT = http -v localhost: 8080 / parser-presadiť JWT = eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJTdG9ybXBhdGgiLCJoYXNNb3RvcmN5Y2xlIjpmYWxzZSwic3ViIjoibXNpbHZlcm1hbiJ9.8LBq2f0eINB34AzhVEgsln_KDo-IyeM8kc-dTzSCr0c HTTP / 1.1 Accept: * / * ...HTTP / 1.1 400 Bad Request Cache-Control: no-cache, no-store, max-age = 0, must-revalidate Pripojenie: zavrieť Content-Type: application / json; charset = UTF-8 ... {"exceptionType" : "io.jsonwebtoken.IncorrectClaimException", "message": "Očakávané tvrdenie hasMotorcycle je: true, ale bolo: false.", "status": "CHYBA"}

To naznačuje, že naše tvrdenie hasMotorcycle bolo prítomné, ale malo hodnotu, ktorá sa neočakávala.

MissingClaimException a IncorrectClaimException sú vaši priatelia pri presadzovaní nárokov vo vašich JWT a funkcia, ktorú má iba knižnica JJWT.

7.2. JWT kompresia

Ak máte veľa sťažností na JWT, môže byť veľké - také veľké, že sa v niektorých prehliadačoch nemusí zmestiť na adresu GET.

Urobme veľký JWT:

http -v POST localhost: 8080 / dynamic-builder-specific \ iss = Stormpath hasMotorcycle: = true sub = msilverman the = quick brown = fox skočil = cez lenivý = pes \ niekde = cez dúhu = cesta hore = vysoko a = sny = sníval si = o

Tu je JWT, ktoré produkuje:

eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJTdG9ybXBhdGgiLCJoYXNNb3RvcmN5Y2xlIjp0cnVlLCJzdWIiOiJtc2lsdmVybWFuIiwidGhlIjoicXVpY2siLCJicm93biI6ImZveCIsImp1bXBlZCI6Im92ZXIiLCJsYXp5IjoiZG9nIiwic29tZXdoZXJlIjoib3ZlciIsInJhaW5ib3ciOiJ3YXkiLCJ1cCI6ImhpZ2giLCJhbmQiOiJ0aGUiLCJkcmVhbXMiOiJ5b3UiLCJkcmVhbWVkIjoib2YifQ.AHNJxSTiDw_bWNXcuh-LtPLvSjJqwDvOOUcmkk7CyZA

Ten prísavník je veľký! Poďme teraz do trochu iného koncového bodu s rovnakými tvrdeniami:

http -v POST localhost: 8080 / dynamic-builder-compress \ iss = Stormpath hasMotorcycle: = true sub = msilverman the = quick brown = fox skočil = cez lenivý = pes \ niekde = cez dúhu = cesta hore = vysoko a = sny = sníval si = o

Tentokrát dostaneme:

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

O 62 znakov menej! Tu je kód metódy použitej na generovanie JWT:

@RequestMapping (value = "/ dynamic-builder-compress", method = POST) public JwtResponse dynamicBuildercompress (@RequestBody Map claims) hodí UnsupportedEncodingException {String jws = Jwts.builder () .setClaims (claims) .compressWith (CompressionCodecs.DEF) .signWith (SignatureAlgorithm.HS256, secretService.getHS256SecretBytes ()) .compact (); vrátiť nové JwtResponse (jws); }

Všimnite si na riadku 6, že špecifikujeme kompresný algoritmus, ktorý sa má použiť. To je všetko.

A čo analýza komprimovaných JWT? Knižnica JJWT automaticky detekuje kompresiu a na dekompresiu používa rovnaký algoritmus:

GET /parser?jwt=eyJhbGciOiJIUzI1NiIsImNhbGciOiJERUYifQ.eNpEzkESwjAIBdC7sO4JegdXnoC2tIk2oZLEGB3v7s84jjse_AFe5FOikc5ZLRycHQ3kOJ0Untu8C43ZigyUyoRYSH6_iwWOyGWHKd2Kn6_QZFojvOoDupRwyAIq4vDOzwYtugFJg1QnJv-5sY-TVjQqN7gcKJ3f-j8c-6J-baDFhEN_uGn58XtnpfcHAAD__w.3_wc-2skFBbInk0YAQ96yGWwr8r1xVdbHn-uGPTFuFE HTTP / 1.1 Accept: * / * ... HTTP / 1.1 200 OK Cache-Control: no-cache, no -store, max-age = 0, must-revalidate Content-Type: application / json; charset = UTF-8 ... {"claims": {"body": {"a": "the", "brown" : "líška", "sníval": "of", "sny": "vy", "hasMotorcycle": pravda, "iss": "Stormpath", "preskočil": "cez", "lenivý": "pes" , "rainbow": "way", "niekde": "over", "sub": "msilverman", "the": "quick", "up": "high"}, "header": {"alg" : "HS256", "calg": "DEF"}, "signature": "3_wc-2skFBbInk0YAQ96yGWwr8r1xVdbHn-uGPTFuFE"}, "status": "ÚSPECH"}

Všimnite si calg nárok v hlavičke. Toto bolo automaticky zakódované do JWT a poskytuje to parseru nápovedu o tom, aký algoritmus sa použije na dekompresiu.

POZNÁMKA: Špecifikácia JWE nepodporuje kompresiu. V pripravovanom vydaní knižnice JJWT budeme podporovať JWE a komprimované JWE. Naďalej budeme podporovať kompresiu v iných typoch JWT, aj keď nie je uvedená.

8. Token Tools for Java Devs

Aj keď hlavným zameraním tohto článku nebolo Spring Boot alebo Spring Security, použitie týchto dvoch technológií uľahčilo demonštráciu všetkých funkcií diskutovaných v tomto článku. Mali by ste byť schopní aktivovať server a začať hrať s rôznymi koncovými bodmi, o ktorých sme hovorili. Stačí kliknúť:

http // localhost: 8080

Stormpath je tiež nadšený, že prináša do komunity Java množstvo vývojových nástrojov otvoreného zdroja. Tie obsahujú:

8.1. JJWT (o čom sme hovorili)

JJWT je ľahko použiteľný nástroj pre vývojárov na vytváranie a overovanie JWT v jazyku Java. Rovnako ako mnoho knižníc, ktoré podporuje Stormpath, je JJWT úplne bezplatný a otvorený zdroj (licencia Apache, verzia 2.0), takže každý môže vidieť, čo robí a ako to robí. Neváhajte nahlásiť akékoľvek problémy, navrhnúť vylepšenia alebo dokonca odoslať nejaký kód!

8.2. jsonwebtoken.io a java.jsonwebtoken.io

jsonwebtoken.io je vývojársky nástroj, ktorý sme vytvorili, aby sme uľahčili dekódovanie súborov JWT. Jednoducho vložte existujúci JWT do príslušného poľa a dekódujte jeho hlavičku, užitočné zaťaženie a podpis. jsonwebtoken.io používa nJWT, najčistejšiu bezplatnú a otvorenú knižnicu JWT pre vývojárov Node.js (licencia Apache, verzia 2.0). Na tejto webovej stránke si tiež môžete pozrieť kód vygenerovaný pre rôzne jazyky. Samotná webová stránka je open-source a nájdete ju tu.

java.jsonwebtoken.io je špeciálne pre knižnicu JJWT. Môžete zmeniť hlavičky a užitočné zaťaženie v pravom hornom poli, pozrieť sa na JWT vygenerovaný JJWT v ľavom hornom poli a v dolných poliach vidieť ukážku Java kódu stavača a analyzátora. Samotná webová stránka je open source a nájdete ju tu.

8.3. Inšpektor JWT

Nové dieťa v rade, JWT Inspector je rozšírenie prehliadača Chrome s otvoreným zdrojovým kódom, ktoré umožňuje vývojárom kontrolovať a ladiť súbory JWT priamo v prehliadači. Inšpektor JWT vyhľadá na vašom webe súbory JWT (v súboroch cookie, miestnom úložisku / relácii a hlavičkách) a ľahko ich sprístupní prostredníctvom navigačného panela a panela DevTools.

9. JWT This Down!

JWT dodávajú obyčajným žetónom trochu inteligencie. Schopnosť kryptograficky podpisovať a overovať, budovať časy vypršania platnosti a kódovať ďalšie informácie do súborov JWT pripravuje pôdu pre správu skutočne bezstavových relácií. To má veľký vplyv na schopnosť škálovať aplikácie.

V Stormpath používame okrem iných použití aj JWT pre tokeny OAuth2, tokeny CSRF a tvrdenia medzi mikroslužbami.

Len čo začnete používať JWT, už sa možno nikdy nevrátite k nemým žetónom minulosti. Máte nejaké otázky? Zrazte ma na @afitnerd na twitteri.


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