Az OAuth 2.0 engedélyezési keretrendszer megvalósítása a Jakarta EE használatával

1. Áttekintés

Ebben az oktatóanyagban az OAuth 2.0 engedélyezési keretrendszer megvalósítását fogjuk biztosítani a Jakarta EE és a MicroProfile használatával. Ami a legfontosabb, hogy az OAuth 2.0 szerepkörök kölcsönhatását az Engedélyezési kód engedélytípuson keresztül valósítjuk meg. Az írás hátterében az áll, hogy támogatást nyújtson a Jakarta EE használatával megvalósított projektekhez, mivel ez még nem nyújt támogatást az OAuth számára.

A legfontosabb szerephez, az Authorization Serverhez, megvalósítani fogjuk az Engedélyezési végpontot, a Token végpontot és ezen felül a JWK Kulcsvégpontot, amely az erőforrás-kiszolgáló számára hasznos a nyilvános kulcs lekéréséhez.

Mivel azt szeretnénk, hogy a megvalósítás egyszerű és könnyű legyen a gyors telepítéshez, az ügyfelek és felhasználók előre regisztrált boltját, valamint nyilvánvalóan egy JWT áruházat fogunk használni a hozzáférési tokenek számára.

Mielőtt közvetlenül a témába ugrana, fontos megjegyezni, hogy az oktatóanyagban szereplő példa oktatási célokat szolgál. Gyártási rendszerek esetében nagyon ajánlott egy érett, jól bevált megoldást használni, mint például a Keycloak.

2. OAuth 2.0 áttekintés

Ebben a szakaszban rövid áttekintést adunk az OAuth 2.0 szerepkörökről és az Engedélyezési kód engedélyezési folyamatáról.

2.1. Szerepek

Az OAuth 2.0 keretrendszer a következő négy szerep együttműködését jelenti:

  • Erőforrás-tulajdonos: Általában ez a végfelhasználó - ez az entitás rendelkezik bizonyos erőforrásokkal, amelyeket érdemes megvédeni
  • Erőforrás-kiszolgáló: Az erőforrás-tulajdonos adatait védő szolgáltatás, általában egy REST API-n keresztül teszi közzé azokat
  • Ügyfél: Az erőforrás-tulajdonos adatait használó alkalmazás
  • Authorization Server: Olyan alkalmazás, amely lejáró tokenek formájában engedélyt vagy jogosultságot ad az ügyfeleknek

2.2. Engedélyezési támogatás típusai

A támogatás típusa az, hogy az ügyfél hogyan kap engedélyt az erőforrás-tulajdonos adatainak felhasználására, végső soron hozzáférési jogkivonat formájában.

Természetesen a különböző típusú ügyfelek a különböző típusú támogatásokat részesítik előnyben:

  • megerősítő kód: Leggyakrabban preferáltaz-e webalkalmazás, natív alkalmazás vagy egyoldalas alkalmazás, bár a natív és egyoldalas alkalmazások további védelmet igényelnek, PKCE néven
  • Token frissítése: Különleges megújítási támogatás, alkalmas webes alkalmazásokhoz hogy megújítsák meglévő jelzőjüket
  • Ügyfél hitelesítő adatok: Előnyben részesítve szolgáltatás-szolgáltatás kommunikáció, mondjuk, ha az erőforrás tulajdonosa nem végfelhasználó
  • Erőforrás-tulajdonosJelszó: A a natív alkalmazások első fél általi hitelesítése, mondjuk, amikor a mobilalkalmazásnak saját bejelentkezési oldalra van szüksége

Ezenkívül az ügyfél használhatja a beleértett támogatás típusa. Azonban általában biztonságosabb az engedélyezési kód megadását használni a PKCE-vel.

2.3. Engedélyezési kód Grant Flow

Mivel az engedélyezési kód megadásának folyamata a leggyakoribb, nézzük át annak működését is valójában ezt építjük fel ebben az oktatóanyagban.

Alkalmazás - kliens - engedélyt kér az átirányítással az engedélyezési szerverhez / engedélyez végpont. Ehhez a végponthoz az alkalmazás a visszahív végpont.

Az engedélyezési szerver általában engedélyt kér a végfelhasználótól - az erőforrás tulajdonosától. Ha a végfelhasználó engedélyt ad, akkor az engedélyezési kiszolgáló visszairányít a visszahívásra val,-vel kód.

Az alkalmazás megkapja ezt a kódot, majd hitelesített hívást kezdeményez a hitelesítési szerver felé /jelképes végpont. A „hitelesített” kifejezés alatt azt értjük, hogy az alkalmazás ennek a hívásnak a részeként bizonyítja, hogy ki az. Ha minden rendben jelenik meg, akkor az engedélyezési kiszolgáló a tokennel válaszol.

A tokennel a kezében, az alkalmazás megküldi kérését az API-nak - az erőforrás-kiszolgáló - és az API ellenőrzi a tokent. Megkérheti az engedélyezési szervert, hogy a token használatával ellenőrizze a tokent /önmagát elemzi végpont. Vagy ha a token önálló, akkor az erőforrás-kiszolgáló optimalizálhatja helyileg ellenőrzi a token aláírását, ahogy a JWT esetében is.

2.4. Mit támogat a Jakarta EE?

Még nem sok. Ebben az oktatóanyagban a legtöbb dolgot alapoktól fogva építjük fel.

3. OAuth 2.0 Authorization Server

Ebben a megvalósításban a következőkre fogunk összpontosítani a leggyakrabban használt támogatási típus: Megerősítő kód.

3.1. Ügyfél- és felhasználói regisztráció

A jogosultságkiszolgálónak természetesen tudnia kell az ügyfelekről és a felhasználókról, mielőtt engedélyezhetné kéréseiket. És gyakran előfordul, hogy egy engedélyezési kiszolgáló rendelkezik ehhez UI-val.

Az egyszerűség kedvéért azonban előre konfigurált klienst fogunk használni:

INSERT INTO kliensek (kliens_azonosító, kliens_ titok, átirányítási_uri, hatókör, engedélyezett_grant_típusok) ÉRTÉKEK ('webappclient', 'webappclientsecret', '// localhost: 9180 / callback', 'resource.read resource.write', 'authorization_code refresh_token');
@Entity @Table (name = "clients") public class Client {@Id @Column (name = "client_id") private String clientId; @Column (name = "client_secret") privát karakterlánc clientSecret; @Column (name = "redirect_uri") private String redirectUri; @ Oszlop (név = "hatókör") privát String hatókör; // ...}

És egy előre konfigurált felhasználó:

INSERT INTO felhasználók (user_id, jelszó, szerepkörök, hatókörök) ÉRTÉKEK ('appuser', 'appusersecret', 'USER', 'resource.read resource.write');
@Entity @Table (name = "users") public class Felhasználó végrehajtja Principal {@Id @Column (name = "user_id") private String userId; @ Oszlop (név = "jelszó") privát karakterlánc jelszó; @ Oszlop (név = "szerepek") privát karakterlánc szerepek; @ Oszlop (név = "hatókörök") privát String hatókörök; // ...}

Ne feledje, hogy az oktatóanyag érdekében a jelszavakat egyszerű szövegben használtuk, de termelési környezetben el kell keverni őket.

A bemutató további részében megmutatjuk, hogyan appuser - az erőforrás tulajdonosa - hozzáférést nyújthat webappclient - az alkalmazás - az Engedélyezési Kód végrehajtásával.

3.2. Engedélyezési végpont

Az engedélyezési végpont fő szerepe az első hitelesítse a felhasználót, majd kérje meg az engedélyeket - vagy hatókörök - amelyeket az alkalmazás kíván.

Az OAuth2 specifikációk utasítása szerint ennek a végpontnak támogatnia kell a HTTP GET metódust, bár támogatja a HTTP POST metódust is. Ebben a megvalósításban csak a HTTP GET módszert támogatjuk.

Első, az engedélyezési végpont megköveteli a felhasználó hitelesítését. A specifikáció itt nem igényel bizonyos módszert, ezért használjuk a Jakarta EE 8 Security API űrlaphitelesítését:

@FormAuthenticationMechanismDefinition (loginToContinue = @LoginToContinue (loginPage = "/login.jsp", errorPage = "/login.jsp"))

A felhasználót a rendszer átirányítja /login.jsp hitelesítéshez, majd a CallerPrincalal az S-en keresztülecurityContext API:

Fő megbízó = securityContext.getCallerPrincipal ();

Ezeket össze tudjuk rakni a JAX-RS segítségével:

@FormAuthenticationMechanismDefinition (loginToContinue = @LoginToContinue (loginPage = "/login.jsp", errorPage = "/login.jsp")) @Path ("engedélyez") nyilvános osztály AuthorizationEndpoint {// ... @GET @Produces (MediaType. TEXT_HTML) public Response doGet (@Context HttpServletRequest kérés, @Context HttpServletResponse válasz, @Context UriInfo uriInfo) dob ServletException, IOException {MultivaluedMap params = uriInfo.getQueryParameters ( Fő megbízó = securityContext.getCallerPrincipal (); // ...}}

Ezen a ponton az engedélyezési végpont elkezdheti feldolgozni az alkalmazás kérését, amelynek tartalmaznia kell response_type és Ügyfélazonosító paraméterek és - opcionálisan, de ajánlott - a redirect_uri, hatókör, és állapot paraméterek.

A Ügyfélazonosító érvényes ügyfélnek kell lennie, esetünkben a ügyfelek adatbázis tábla.

A átirányítás_uri, ha meg van adva, akkor meg kell egyeznie azzal is, amit a ügyfelek adatbázis tábla.

És mivel engedélyezési kódot csinálunk, response_type van kód.

Mivel az engedélyezés többlépcsős folyamat, átmenetileg ezeket az értékeket tárolhatjuk a munkamenetben:

request.getSession (). setAttribute ("ORIGINAL_PARAMS", paraméterek);

Ezután készüljön fel arra, hogy megkérdezze a felhasználótól, mely engedélyeket használhatja az alkalmazás, átirányítva az adott oldalra:

String allowedScopes = checkUserScopes (user.getScopes (), requiredScope); request.setAttribute ("hatókörök", allowedScopes); request.getRequestDispatcher ("/ authorize.jsp"). továbbítás (kérés, válasz);

3.3. Felhasználói hatókörök jóváhagyása

Ezen a ponton a böngésző engedélyezési felhasználói felületet jelenít meg a felhasználó számára, és a felhasználó kiválaszt. Ezután a böngésző ben adja be a felhasználó választását egy HTTP POST:

@POST @Consumes (MediaType.APPLICATION_FORM_URLENCODED) @Produces (MediaType.TEXT_HTML) nyilvános válasz doPost (@Context HttpServletRequest kérés, @Context HttpServletResponse válasz, MultivaluedMap params) dobja Exception {MultivaluedMap (RequestPlay) "ORIGINAL_PARAMS"); // ... Karakterlánc-jóváhagyási állapot = params.getFirst ("jóváhagyás-állapot"); // IGEN VAGY NEM // ... ha IGEN Felsorolja a jóváhagyottScopes = params.get ("hatókör") listát; // ...}

Ezután létrehozunk egy ideiglenes kódot, amely a user_id, client_id, ésredirect_uri, mindezt az alkalmazás később fogja használni, amikor eléri a token végpontot.

Tehát hozzunk létre egy Megerősítő kód JPA entitás automatikusan létrehozott azonosítóval:

@Entity @Table (name) public class AuthorizationCode {@Id @GeneratedValue (strategy = GenerationType.AUTO) @Column (name = "code") private String code; // ...}

És akkor töltse be:

AuthorizationCode authorityCode = új AuthorizationCode (); authorizationCode.setClientId (clientId); authorizationCode.setUserId (userId); authorizationCode.setApprovedScopes (String.join ("", authorisedScopes)); authorityCode.setExpirationDate (LocalDateTime.now (). plusMinutes (2)); authorizationCode.setRedirectUri (redirectUri);

Amikor elmentjük a babot, a kód attribútum automatikusan kitöltődik, így megszerezhetjük és visszaküldhetjük az ügyfélnek:

appDataRepository.save (authenticationCode); Karakterlánc-kód = authenticationCode.getCode ();

Vegye figyelembe, hogy engedélyezési kódunk két perc múlva lejár - minél konzervatívabbaknak kell lennünk ezzel a lejárattal. Rövid lehet, mivel az ügyfél azonnal hozzáférési tokenre cseréli.

Ezután átirányítunk az alkalmazáshoz redirect_uri, megadva neki a kódot, mint bármely más állapot paraméter, amelyet az alkalmazás a / engedélyez kérés:

StringBuilder sb = új StringBuilder (redirectUri); // ... sb.append ("? code ="). append (kód); Karaktersorozat = params.getFirst ("állapot"); if (állapot! = null) {sb.append ("& state ="). append (állapot); } URI helye = UriBuilder.fromUri (sb.toString ()). Build (); return Response.seeOther (hely) .build ();

Megjegyezzük még egyszer redirectUri bármi létezik a ügyfelek táblázat, nem pedig a átirányítás_uri kérés paraméter.

Tehát a következő lépésünk az, hogy az ügyfél megkapja ezt a kódot, és kicseréli egy hozzáférési tokenné a token végpont használatával.

3.4. Token Endpoint

Az engedélyezési végponttal szemben a token végpont nincs szüksége böngészőre az ügyféllel való kommunikációhoz, és ezért JAX-RS végpontként fogjuk megvalósítani:

@Path ("token") nyilvános osztály TokenEndpoint {Lista támogatottGrantTypes = Collections.singletonList ("jogosultsági kód"); @ Inject private AppDataRepository appDataRepository; @ Inject példány engedélyezéseGrantTypeHandlers; @POST @Produces (MediaType.APPLICATION_JSON) @Consumes (MediaType.APPLICATION_FORM_URLENCODED) public Response token (Többértékű térkép paraméterek, @HeaderParam (HttpHeaders.AUTHORIZATION) String authHeader) // JOSEException

A token végpont POST-ot igényel, valamint a paramétereket a application / x-www-form-urlencoded média típus.

Ahogy megbeszéltük, csak a megerősítő kód támogatás típusa:

ListagedGrantTypes = Collections.singletonList ("jogosultsági_kód");

Szóval, a fogadott grant_type mint szükséges paramétert támogatni kell:

String grantType = params.getFirst ("grant_type"); Objects.requireNonNull (grantType, "a grant_type paraméterekre van szükség"); if (! supportGrantTypes.contains (grantType)) {JsonObject hiba = Json.createObjectBuilder () .add ("hiba", "unsupported_grant_type") .add ("error_description", "a támogatás típusának a következők egyikének kell lennie:" + támogatottGrantTypes). épít(); return Válasz.állapot (Válasz.állapot.BAD_REQUEST) .entity (hiba) .build (); }

Ezután a kliens hitelesítését a HTTP Basic hitelesítéssel ellenőrizzük. Vagyis ellenőrizzük ha a kapott Ügyfélazonosító és client_secret, keresztül Engedélyezés fejléc, megfelel egy regisztrált ügyfélnek:

Karakterlánc [] clientCredentials = kivonat (authHeader); Karakterlánc clientId = clientCredentials [0]; Karakterlánc clientSecret = clientCredentials [1]; Ügyfél kliens = appDataRepository.getClient (clientId); if (kliens == null || clientSecret == null ||! clientSecret.equals (client.getClientSecret ())) {JsonObject hiba = Json.createObjectBuilder () .add ("hiba", "érvénytelen_client") .build () ; return Válasz.állapot (Válasz.állapot.NEM Engedélyezett) .entity (hiba) .build (); }

Végül delegáljuk a TokenResponse a megfelelő támogatástípus-kezelőnek:

nyilvános felület AuthorizationGrantTypeHandler {TokenResponse createAccessToken (String clientId, MultivaluedMap paraméterek) dobja a Kivételt; }

Mivel jobban érdekel bennünket az engedélyezési kód megadásának típusa, megfelelő megvalósítást biztosítottunk CDI-babként és díszítettük a Nevezett kommentár:

@Név ("jogosultsági kód")

Futás közben, és a beérkezett szerint grant_type érték, a megfelelő megvalósítást a CDI példány mechanizmusa aktiválja:

String grantType = params.getFirst ("grant_type"); // ... AuthorizationGrantTypeHandler authorizationGrantTypeHandler = authorityGrantTypeHandlers.select (NamedLiteral.of (grantType)). Get ();

Itt az ideje a termelésnek /jelképesVálasza.

3.5. RSA Privát és nyilvános kulcsok

A token előállítása előtt szükségünk van egy RSA magánkulcsra a tokenek aláírásához.

Erre a célra az OpenSSL-t fogjuk használni:

# A PRIVÁT KULCS megnyitja az gens kulcs -algoritmust az RSA -out -out private-key.pem -pkeyopt rsa_keygen_bits: 2048

A magánkulcs.pem a kiszolgálóhoz a MicroProfile Config szolgáltatáson keresztül érkezik signingKey tulajdonság a fájl segítségével META-INF / microprofile-config.properties:

signingkey = / META-INF / private-key.pem

A szerver az injekció segítségével elolvashatja a tulajdonságot Konfig tárgy:

String signingkey = config.getValue ("signingkey", String.class);

Hasonlóképpen előállíthatjuk a megfelelő nyilvános kulcsot is:

# PUBLIC KEY openssl rsa -pubout -in private-key.pem -out public-key.pem

És használja a MicroProfile Config alkalmazást VerzióKulcs hogy elolvassa:

ellenőrző kulcs = / META-INF / public-key.pem

A szervernek elérhetővé kell tennie az erőforrás-kiszolgáló számára az ellenőrzés célja. Ez kész van egy JWK végponton keresztül.

Nimbus JOSE + JWT egy könyvtár, amely itt nagy segítség lehet. Először tegyük hozzá a nimbus-jose-jwt függőség:

 com.nimbusds nimbus-jose-jwt 7.7 

És most kihasználhatjuk a Nimbus JWK támogatását a végpontunk egyszerűsítése érdekében:

@Path ("jwk") @ApplicationScoped nyilvános osztály JWKEndpoint {@GET public Response getKey (@QueryParam ("format") Karakterlánc formátum) dobja a Kivételt {// ... Karakterlánc verifikációs kulcs = config.getValue ("verifikációs kulcs", String. osztály); String pemEncodedRSAPublicKey = PEMKeyUtils.readKeyAsString (ellenőrzőkulcs); if (format == null || formátum.egyenlő ("jwk")) {JWK jwk = JWK.parseFromPEMEncodedObjects (pemEncodedRSAPublicKey); return Response.ok (jwk.toJSONString ()). type (MediaType.APPLICATION_JSON) .build (); } else if (format.equals ("pem")) {return Response.ok (pemEncodedRSAPublicKey) .build (); } // ...}}

Használtuk a formátumot paraméter a PEM és a JWK formátum közötti váltáshoz. Az erőforrás-kiszolgáló megvalósításához használt MicroProfile JWT mindkét formátumot támogatja.

3.6. Token Endpoint Response

Itt az ideje egy adottnak AuthorizationGrantTypeHandler hogy létrehozza a token választ. Ebben a megvalósításban csak a strukturált JWT tokeneket támogatjuk.

Az ilyen formátumú tokenek létrehozásához ismét a Nimbus JOSE + JWT könyvtárat fogjuk használni, de számos más JWT könyvtár is létezik.

Tehát egy aláírt JWT létrehozásához először össze kell állítanunk a JWT fejlécet:

JWSHeader jwsHeader = új JWSHeader.Builder (JWSAlgorithm.RS256) .type (JOSEObjectType.JWT) .build ();

Ezután felépítjük a hasznos terhet ami a Készlet szabványosított és egyedi igények:

Instant now = Instant.now (); Hosszú ideje lejárInMin = 30L; Date in30Min = Date.from (now.plus (expiresInMin, ChronoUnit.MINUTES)); JWTClaimsSet jwtClaims = new JWTClaimsSet.Builder () .issuer ("// localhost: 9080") .subject (authenticationCode.getUserId ()) .claim ("upn", authorityCode.getUserId ()) .audience ("// localhost: 9280 ". from (now)) .issueTime (Date.from (now)) .jwtID (UUID.randomUUID (). toString ()) .build (); AláírtJWT aláírtJWT = új AláírtJWT (jwsHeader, jwtClaims);

A szokásos JWT igények mellett még két követelést is felvettünk - upn és csoportok - ahogyan a MicroProfile JWT-nek szüksége van rájuk. A upn leképezik a Jakarta EE Security-re CallerPrincalal és a csoportok térképezni fogják Jakarta EE-hez Szerepek.

Most, hogy megvan a fejléc és a hasznos teher, alá kell írnunk a hozzáférési tokent egy RSA magánkulccsal. A megfelelő RSA nyilvános kulcs a JWK végponton keresztül lesz kitéve, vagy más módon elérhetővé válik, hogy az erőforrás-kiszolgáló felhasználhassa a hozzáférési token ellenőrzésére.

Mivel a magánkulcsot PEM formátumban biztosítottuk, be kell szereznünk és átalakítanunk egy RSAPrivateKey:

AláírtJWT aláírtJWT = új AláírtJWT (jwsHeader, jwtClaims); // ... Karakterlánc signingkey = config.getValue ("signingkey", String.class); Karaktersorozat pemEncodedRSAPrivateKey = PEMKeyUtils.readKeyAsString (aláíró kulcs); RSAKey rsaKey = (RSAKey) JWK.parseFromPEMEncodedObjects (pemEncodedRSAPrivateKey);

Következő, aláírjuk és sorosítjuk a JWT-t:

aláírtJWT.sign (új RSASSASigner (rsaKey.toRSAPrivateKey ())); String accessToken = aláírtJWT.serialize ();

És végül létrehozunk egy token választ:

return Json.createObjectBuilder () .add ("token_type", "Bearer") .add ("access_token", accessToken) .add ("expires_in", expiresInMin * 60) .add ("hatókör", authenticationCode.getApprovedScopes ()) .épít();

amelyet a JSON-P-nek köszönhetően JSON formátumba sorosítottak és elküldtek az ügyfélnek:

{"access_token": "acb6803a48114d9fb4761e403c17f812", "token_type": "Bearer", "expires_in": 1800, "hatókör": "resource.read resource.write"}

4. OAuth 2.0 kliens

Ebben a részben mi leszünk webalapú OAuth 2.0 kliens kiépítése a Servlet, a MicroProfile Config és a JAX RS Client API-k használatával.

Pontosabban két fő szervletet fogunk megvalósítani: az egyiket az engedélyezési kiszolgáló engedélyezési végpontjának kérésére és egy kód megszerzésére az engedélyezési kód megadásának típusával, egy másik kiszolgálót pedig a fogadott kód használatára és hozzáférési token kérésére az engedélyezési kiszolgáló token végpontjától. .

Ezenkívül még két kiszolgáló kisalkalmazást valósítunk meg: az egyiket egy új hozzáférési token megszerzéséhez a frissítési token engedély típusával, a másikat pedig az erőforrás-kiszolgáló API-jainak eléréséhez.

4.1. OAuth 2.0 kliens adatai

Mivel az ügyfél már regisztrálva van az engedélyezési kiszolgálón, először meg kell adnunk az ügyfél regisztrációs adatait:

  • Ügyfélazonosító: Ügyfél-azonosító, és általában a hitelesítési szerver adja ki a regisztráció során.
  • client_secret: Ügyféltitok.
  • redirect_uri: Az a hely, ahol megkapja az engedélyezési kódot.
  • hatókör: Az ügyfél engedélyeket kért.

Ezenkívül az ügyfélnek ismernie kell a hitelesítési kiszolgáló hitelesítési és token végpontjait:

  • autorizációs_uri: A hitelesítési kiszolgáló engedélyezési végpontjának helye, amelyet felhasználhatunk kód megszerzéséhez.
  • token_uri: A hitelesítési kiszolgáló token végpontjának helye, amelyet felhasználhatunk token megszerzésére.

Ezeket az információkat a MicroProfile Config fájl biztosítja, META-INF / microprofile-config.properties:

# Ügyfélregisztráció client.clientId = webappclient client.clientSecret = webappclientsecret client.redirectUri = // localhost: 9180 / visszahívás client.scope = resource.read resource.write # Provider szolgáltató.authorizationUri = // 127.0.0.1:9080/ szolgáltató engedélyezése .tokenUri = // 127.0.0.1:9080/token

4.2. Engedélyezési kód kérése

A hitelesítési kód megszerzésének folyamata azzal kezdődik, hogy az ügyfél átirányítja a böngészőt az engedélyezési kiszolgáló engedélyezési végpontjához.

Ez általában akkor történik, amikor a felhasználó engedély nélkül, vagy kifejezetten az ügyfél meghívásával próbál hozzáférni egy védett erőforrás API-hoz / engedélyez pálya:

@WebServlet (urlPatterns = "/ authorize") nyilvános osztály A AuthorizationCodeServlet kiterjeszti a HttpServlet {@Inject private Config config; @Orride védett void doGet (HttpServletRequest kérés, HttpServletResponse válasz) dobja a ServletException, IOException {// ...}}

Ban,-ben doGet () módszerrel egy biztonsági állapotérték előállításával és tárolásával kezdjük:

String állapot = UUID.randomUUID (). ToString (); request.getSession (). setAttribute ("CLIENT_LOCAL_STATE", állapot);

Ezután lekérjük az ügyfél konfigurációs adatait:

Karakterlánc engedélyezéseUri = config.getValue ("szolgáltató.authorizationUri", Karakterlánc.osztály); Karakterlánc clientId = config.getValue ("client.clientId", String.class); String redirectUri = config.getValue ("client.redirectUri", String.class); Karakterlánc hatóköre = config.getValue ("client.scope", String.class);

Ezután ezeket az információkat lekérdezési paraméterként csatoljuk a hitelesítési kiszolgáló engedélyezési végpontjához:

Karakterlánc-felhatalmazásLocation = AuthorUri + "? Response_type = code" + "& client_id =" + clientId + "& redirect_uri =" + redirectUri + "& scope =" + hatókör + "& state =" + állapot;

És végül átirányítjuk a böngészőt erre az URL-re:

response.sendRedirect (authorizationLocation);

A kérelem feldolgozása után az engedélyezési kiszolgáló engedélyezési végpontja generál és csatol egy kódot, a kapott állapotparaméter mellett az átirányítás_uri és visszairányítja a böngészőt // localhost: 9081 / visszahívás? code = A123 & state = Y.

4.3. Hozzáférési token kérés

Az ügyfél visszahívási szervlet, /visszahív, a beérkezett érvényesítésével kezdődik állapot:

String localState = (Karaktersorozat) request.getSession (). GetAttribute ("CLIENT_LOCAL_STATE"); if (! localState.equals (request.getParameter ("állapot"))) {request.setAttribute ("hiba", "Az állapot attribútum nem egyezik!"); feladás ("/", kérés, válasz); Visszatérés; }

Következő, a korábban kapott kódot használjuk hozzáférési token kérésére az engedélyezési kiszolgáló token végpontján keresztül:

Karaktersorozat = request.getParameter ("kód"); Ügyfél kliens = ClientBuilder.newClient (); WebTarget target = client.target (config.getValue ("szolgáltató.tokenUri", String.osztály)); Forma űrlap = new Form (); form.param ("támogatás_típus", "jogosultsági kód"); form.param ("kód", kód); form.param ("redirect_uri", config.getValue ("client.redirectUri", String.class)); TokenResponse tokenResponse = target.request (MediaType.APPLICATION_JSON_TYPE). Header (HttpHeaders.AUTHORIZATION, getAuthorizationHeaderValue ()) .post (Entity.entity (form, MediaType.APPLICATION_FORM_URLENCOD_T_T_TYPE)

Mint láthatjuk, nincs böngésző interakció ehhez a híváshoz, és a kérést közvetlenül a JAX-RS kliens API-val, HTTP POST-ként használják.

Mivel a token végpont megköveteli az ügyfél hitelesítését, belefoglaltuk az ügyfél hitelesítő adatait Ügyfélazonosító és client_secret ban,-ben Engedélyezés fejléc.

Az ügyfél ezt a hozzáférési jogkivonatot használhatja a következő alszakasz tárgyát képező erőforrás-kiszolgáló API-k meghívására.

4.4. Védett erőforrás-hozzáférés

Ezen a ponton érvényes hozzáférési jogkivonattal rendelkezünk, és felhívhatjuk az erőforrás-kiszolgáló /olvas és /ír API-k.

Ehhez, biztosítanunk kell a Engedélyezés fejléc. A JAX-RS Client API használatával ez egyszerűen a Invocation.Builder fejléc () módszer:

resourceWebTarget = webTarget.path ("erőforrás / olvasás"); Invocation.Builder invocationBuilder = resourceWebTarget.request (); response = invocationBuilder .header ("hitelesítés", tokenResponse.getString ("access_token")) .get (String.osztály);

5. OAuth 2.0 erőforrás-kiszolgáló

Ebben a szakaszban egy biztonságos webalkalmazást építünk a JAX-RS, a MicroProfile JWT és a MicroProfile Config alapján. A MicroProfile JWT gondoskodik a beérkezett JWT érvényesítéséről és a JWT hatókörök leképezéséről Jakarta EE szerepkörökre.

5.1. Maven-függőségek

A Java EE Web API-függőség mellett szükségünk van a MicroProfile Config és a MicroProfile JWT API-kre is:

 javax javaee-web-api 8.0 biztosított org.eclipse.microprofile.config microprofile-config-api 1.3 org.eclipse.microprofile.jwt microprofile-jwt-auth-api 1.1 

5.2. JWT hitelesítési mechanizmus

A MicroProfile JWT biztosítja a Bearer Token Authentication mechanizmus megvalósítását. Ez gondoskodik a JWT jelen lévő feldolgozásáról Engedélyezés fejléc, elérhetővé teszi a Jakarta EE biztonsági igazgatóját a JsonWebToken amely birtokolja a JWT követeléseket, és leképezi a hatóköröket a Jakarta EE szerepekre. Tekintse meg a Jakarta EE Security API-t további háttérképekért.

A JWT hitelesítési mechanizmus a kiszolgálón, nekünk kell Add hozzá a LoginConfig annotáció a JAX-RS alkalmazásban:

@ApplicationPath ("/ api") @DeclareRoles ({"resource.read", "resource.write"}) @LoginConfig (authMethod = "MP-JWT") nyilvános OAuth2ResourceServerApplication osztály kiterjeszti az alkalmazást {}

Ezenkívül A MicroProfile JWT-nek az RSA nyilvános kulcsra van szüksége a JWT aláírásának ellenőrzéséhez. Ezt biztosíthatjuk önellenőrzéssel, vagy az egyszerűség kedvéért úgy, hogy kézzel másoljuk a kulcsot az engedélyezési szerverről. Mindkét esetben meg kell adnunk a nyilvános kulcs helyét:

mp.jwt.verify.publickey.location = / META-INF / public-key.pem

Végül a MicroProfile JWT-nek ellenőriznie kell a iss a bejövő JWT igénye, amelynek jelen kell lennie, és meg kell egyeznie a MicroProfile Config tulajdonság értékével:

mp.jwt.verify.issuer = // 127.0.0.1:9080

Általában ez az engedélyezési kiszolgáló helye.

5.3. A biztonságos végpontok

Bemutató célokra hozzáadunk egy erőforrás API-t két végponttal. Az egyik a olvas végpont, amelyhez hozzáférhetnek a erőforrás.olvasott hatálya és egy másik ír végpont a erőforrás.írja hatálya.

A hatókörök korlátozása a @RolesAllowed kommentár:

@Path ("/ resource") @RequestScoped public class ProtectedResource {@Inject private JsonWebToken igazgató; @GET @RolesAllowed ("resource.read") @Path ("/ read") public String read () {return "Védett erőforrás, amelyet elér:" + fő.getNév (); } @POST @RolesAllowed ("resource.write") @Path ("/ write") public String write () {return "Védett erőforrás, amelyet elérhet:" + main.getName (); }}

6. Az összes szerver futtatása

Egy szerver futtatásához csak meg kell hívnunk a Maven parancsot a megfelelő könyvtárban:

mvn package liberty: run-server

A hitelesítési kiszolgáló, az ügyfél és az erőforrás-kiszolgáló futni fog, és elérhető lesz a következő helyeken:

# Authorization Server // localhost: 9080 / # Client // localhost: 9180 / # Resource Server // localhost: 9280 / 

Tehát elérhetjük az ügyfél kezdőlapját, majd a „Hozzáférési token letöltése” gombra kattintva elindíthatjuk az engedélyezési folyamatot. Miután megkapta a hozzáférési tokent, hozzáférhetünk az erőforrás-kiszolgálóhoz olvas és ír API-k.

A megadott hatókörektől függően az erőforrás-kiszolgáló vagy sikeres üzenettel válaszol, vagy HTTP 403 tiltott állapotot kapunk.

7. Következtetés

Ebben a cikkben bemutattuk egy OAuth 2.0 hitelesítési kiszolgáló megvalósítását, amely bármely kompatibilis OAuth 2.0 ügyfél- és erőforrás-kiszolgálóval használható.

Az átfogó keretrendszer ismertetéséhez megvalósítást is biztosítottunk az ügyfél és az erőforrás-kiszolgáló számára. Ezen összetevők megvalósításához a Jakarta EE 8 API-kat használtuk, különösen a CDI, a Servlet, a JAX RS, a Jakarta EE Security alkalmazásokat. Ezenkívül a MicroProfile: MicroProfile Config és MicroProfile JWT pszeudo-Jakarta EE API-kat használtuk.

A példák teljes forráskódja elérhető a GitHub oldalon. Ne feledje, hogy a kód tartalmaz példát mind az engedélyezési kódra, mind a token engedély típusainak frissítésére.

Végül fontos tisztában lenni a cikk oktatási jellegével és azzal, hogy a megadott példát nem szabad felhasználni a termelési rendszerekben.