paint-brush
Zergatik da JWT zure enpresa-aplikazioa ziurtatzeko gakoa - eta zergatik izan daiteke KumuluzEE zure lagun onena berriaarabera@jesperancinha
Historia berria

Zergatik da JWT zure enpresa-aplikazioa ziurtatzeko gakoa - eta zergatik izan daiteke KumuluzEE zure lagun onena berria

arabera João Esperancinha40m2025/01/17
Read on Terminal Reader

Luzeegia; Irakurri

'JWT' edo JavaScript Object Notation Web Token [RFC7519]-n definitutako estandar bat da. Hainbat modutan definitu daiteke eta bi alderdien artean informazioa transmititzeko erabil daiteke. Artikulu honetan, `JWT` Java enpresa-aplikazio arrunt batean nola integratu ikusiko dugu.
featured image - Zergatik da JWT zure enpresa-aplikazioa ziurtatzeko gakoa - eta zergatik izan daiteke KumuluzEE zure lagun onena berria
João Esperancinha HackerNoon profile picture
0-item

Gaur egun, gero eta kezka gehiago ditugu errendimenduaren inguruan, eta, aldi berean, sistemak nola komunika daitezkeen azkar eta fidagarritasunez jakin nahi dugu. Askotan informazioa bidali nahi dugu eta ahal den neurrian isilpean eta seguru mantendu. Datu sentikorrak batzuetan sarean publikoki mugitu behar dira eta hariaren beste muturrean ekintzak eragin behar dituzte. Gehienbat, datuen mutazioak eragingo dituzten ekintzak sortu nahi ditugu. Kasu hauetan, ez dugu soilik gure datuak babestea bilatzen. Gure datuak bidaltzeak abiarazitako ekintzak fidagarriak direla ziurtatu nahi dugu. Gure datuak hainbat modutan babes ditzakegu. Gehienetan, datuak TLS (Transport Layer Security) konexio seguru baten bidez bidaltzen ditugu. Horrek gure datuak kable bidez enkriptatuko direla ziurtatuko du. Ziurtagiriak erabiltzen ditugu bi alderdien arteko konfiantzazko harremanak sortzeko eta hori lortzeko. Artikulu honetan, JWT estandarrari buruz eztabaidatu nahi dut eta gehiago ikusi nahi dut nola integra dezakegun JWT Enterprise aplikazio komun batean. Kasu honetan, KumuluzEE begiratuko diogu .Ikus ditzagun oinarrizko kontzeptu batzuk. JWT edo JSON Web Token, edo hobeto esanda, JavaScript Object Notation Web Token, RFC7519 -n definitutako estandarra da. Estandar hau, RFC (Request For Comments) estandar guztiak bezala, IETF (Internet Engineering Task Force) definitu, idatzi eta argitaratu da. Hainbat modutan defini daiteke. Orokorrean, JWT bi alderdien arteko erreklamazioak transmititzeko forma trinko eta segurua dela esan dezakegu. Erreklamazio bat zer den sinplifikatzeko modu bat, funtsean, informazioa duen izen/balio bikote gisa deskribatzea da. Informazio hau gure Interneteko komunikazioaren alderdi garrantzitsu batzuk bermatzeko behar dugu. Jasotzen dugun informazioa lehen momentuan baliozkotua eta fidagarria dela ziurtatu behar dugu. Orduan balioztatu behar dugu. Hau da, funtsean. Estandar hau ezartzeko, Java enpresa-aplikazio bat inplementatzen lagun diezaguketen hainbat esparru erabil ditzakegu. Spring Boot asko erabiltzen ari da. Askotan, beste izen baten azpian biltzen da erakunde jakin batzuen software egokietan, hala nola bankuak eta beste finantza-erakunde batzuk. Gure adibiderako, zerbait ezberdina egitea erabaki nuen. Spring Boot-en ordez, KumuluzEE adibide bat ikusiko dugu. Kontua da JWT zer den eta nolakoa den zehazki identifikatzea. Java Enterprise Aplikazioak, funtsean, aplikazio-zerbitzari batean inplementa daitezkeen edo beren kabuz exekutatu daitezke txertatutako zerbitzari baten bidez. Adibide gisa, Spring Boot aplikazioak Tomcat zerbitzari txertatu batean exekutatzen dira. Artikulu honetan KumuluzEE jarriko dugu arreta. Spring Boot bezala, zerbitzari txertatu bat ere badu. Kasu honetan Jetty deitzen dela izan ezik. Hau Weld-ekin batera erabiltzen da CDI (Context Dependency Injection) emateko. Java EE eta Jakarta EE teknologia estandar guztiak bateragarriak dira framework honekin.

2. Kasu Adibidea


JWT bere oinarrizko forman nola funtzionatzen duen adierazteko, aurkezteko modu bat pentsatu behar izan nuen. Segurtasuna kezkatzen duten adibide klasikoak bankuak dira. Hala ere, JWT nola funtzionatzen duen erakusteko banku-aplikazio oso bat egitea denbora galtzea litzateke eta agian kontzeptu gehiegi sartuko lirateke. Horren ordez, nik egin dudana oso banku sistema sinple bat da. Gure kezka nagusia datuak hari bidez nola pasatzen diren eta erabiltzaileek gure aplikazioko zenbait eremutara nola atzitzen duten erakustea da. Ez dut eztabaidatuko TLS edo nola bidal dezakegun informazioa enkriptatutako hari bidez. JWT n arreta bere horretan mantenduko dugu. Gure kasua natura eta ingurumena defendatzen dituen talde batek erabiltzen duen banku-sistema da. JWT nola funtzionatzen duen erakusteko modu dibertigarri bat besterik ez da. Naturaren Liga honen pertsonaia nagusia Lucy da, nire artikulu guztietan ohiko pertsonaia bilakatzen ari dena.

3. Arkitektura

Hasi baino lehen, zirriborratu dezagun martxan dagoen gure aplikazioa. Oso aplikazio sinplea da, baina hala ere ona da marraztea:

Hau hain erraza den arrazoia zera da: JWT eskaera guztietan egiaztatzen denez eta eskaera guztiak gako publikoaren aurka egiaztatzen direnez, badakigu eskaera guztietan token zuzena bidaltzen dugun bitartean lortu ahal izango dugula. JWT OAuth2, Okta SSO edo beste edozein baimen-mekanismorekin integra daiteke. Kasu honetan, egiten ari garena autentifikazioa eta baimena ezartzea da. Gure aplikazioan, JWT erabiliko dugu eta honekin batera, gure mezua sinadura erabiliz autentifikatuko dugu. Hala ere, ez dugu aplikazioan sartuko. Horren ordez, erabiltzaileei baimena emango diegu gure aplikazioa erabiltzeko autentifikazio arrakastatsuaren ondoren. Une honetan, erraza da ikustea JWT bere oinarrian aplikazio oso baten zati oso txikia dela. Hala ere, funtzionalitate batzuk gehitu behar dira. Hauek dira behar ditugun baliabideak:

  • Oreka sistema
  • Kreditu sistema


Esan dezagun gure oinarrizko sistemak dirua eta kreditu eskaerak soilik erregistratuko dituela. Funtsean, balioak pilatuko ditu. Demagun, gainera, pertsona batzuk kreditua lortzeko gai izango direla eta beste batzuk ez. Pertsona batzuek dirua gordetzeko aukera izango dute eta beste batzuek kreditua eskuratu ahal izango dute.

4. Teknologiak hautatzea

Sarreran esan bezala, KumuluzEE gure enpresa-aplikazio-esparru gisa erabiliko dugu, eta ultra-oinarrizko aplikazio bat ezarriko dugu, oinarrizko JWT terminologia eta kontzeptuak aztertu ahal izateko. Ziurtatu Java bertsio zuzena duzula. Fase honetan, gutxieneko Java 17 SDK instalatu beharko dugu. Maven, git, IntelliJ bezalako Java-rekin bateragarria den IDE bat eta nolabaiteko shell bat beharko ditugu.

5. Konfigurazioa

Gure aplikazioa hasteko, KumuluzEE mendekotasun batzuk ditugu. Hau da, batez ere, KumuluzEE , Spring Bootek menpekotasun pare bat behar duelako. Ikus dezagun labur-labur POM fitxategia:

 <dependencies> <dependency> <groupId>com.kumuluz.ee.openapi</groupId> <artifactId>kumuluzee-openapi-mp</artifactId> </dependency> <dependency> <groupId>com.kumuluz.ee.openapi</groupId> <artifactId>kumuluzee-openapi-mp-ui</artifactId> </dependency> <dependency> <groupId>com.kumuluz.ee</groupId> <artifactId>kumuluzee-microProfile-3.3</artifactId> </dependency> <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-core</artifactId> </dependency> <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-classic</artifactId> </dependency> <dependency> <groupId>org.jetbrains.kotlin</groupId> <artifactId>kotlin-stdlib</artifactId> </dependency> <dependency> <groupId>org.assertj</groupId> <artifactId>assertj-core</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>io.mockk</groupId> <artifactId>mockk-jvm</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>com.ninja-squad</groupId> <artifactId>springmockk</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>io.kotest</groupId> <artifactId>kotest-assertions-core-jvm</artifactId> <scope>test</scope> </dependency> </dependencies>

Azter ditzagun mendekotasun batzuk laburki. Hau irakurtzen duzun bitartean, jarraitu gure pom.xml fitxategia goitik behera. Hau garrantzitsua da ondorengo azalpena ulertzeko. Mendekotasun pakete bat behar dugu gure aplikazioak funtziona dezan. , Zorionez, KumuluzEE , aplikazio hau abiarazteko oinarrizko sorta estandarrak dituzten Microprofile liburutegiak eskaintzen dizkigu. Hau guztia KumuluzEE -Microprofile liburutegian dago. Gure aplikazioa behar ditugun JWT parametro guztiekin konfiguratu ahal izateko, MicroProfile liburutegi bat gehitu behar diogu. Aldi berean, JSON prozesatzeko liburutegia behar dugu. Hau izango da Johnson Corek egiten duena. KumuluzEE muina behar dugu, noski, lan egiteko. Jetty KumuluzEE esparrua exekutatzen duen azpiko zerbitzaria da. Horregatik behar dugu gure menpekotasunetan. CDI behar dugula kontuan hartuta, hori onartzen duen liburutegi bat ere behar dugu. Gure REST amaierako puntuak gaitzeko, KumuluzEE -ren gainerako liburutegia behar dugu. Gure APIa lortzeko, Geronimo liburutegia behar dugu. Honek JSR-374 inplementazioa eskuragarri dugula ziurtatuko du. Gure JWT eta bere JSON-formatted edukiak ere interpretatu behar ditugu. Lombok ez da berez beharrezkoa. Dena ederra eta distiratsua besterik ez du egiten! Logback-a ere garrantzitsua da erregistroak hobeto interpretatu eta gure emaitzak ulertzeko. Ikus dezagun orain gure resources karpetari. Hasteko, uler dezagun zer espero dugun karpeta honetan. Gure aplikazioa JWT , Logback-ekin erlazionatutako zerbaitekin konfiguratu behar dugu eta azkenik, sortuko ditugun babarrunei buruz zerbait esan behar dugu. Ikus dezagun han dagoen fitxategirik errazena. Beans.xml META-INF-en aurki daiteke:

 <beans xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/beans_1_1.xsd" xmlns:weld="http://jboss.org/schema/weld/beans" bean-discovery-mode="all"> <weld:scan> <weld:exclude name="org.jesperancinha.fintech.model.Accounts"/> </weld:scan> </beans>

Hau ohiko fitxategi bat besterik ez da eta orain pentsatzen ari zaren moduan, fitxategi zahar samarra. Une honetan, KumuluzEE martxan jartzea besterik ez da ideia. Baztertzeko ekintza bat dugu. Honek Weld-i esaten dio ez ditzala klaseko Kontuak kontuan hartzeko babarrunen ekintza eskaneatzeko. Hau garrantzitsua da, erabiltzen ari garen inplementazioarekin, Weld funtsean konstruktore hutsa duten klase guztiak bean gisa hartuko ditu kontuan. Aurrerago ikusiko dugu zergatik ez dugun nahi Kontuak babarruntzat hartzea. Momentuz kontuan izan dezagun Eskaeraren esparruan eskaerak egiten ari garela. Hau logikoa da eskaera bakoitzak beste erabiltzaile bat izan dezakeelako. Ikus dezagun orain nola inplementatzen den " logback ". META-INF en ere aurkitzen da:

 <configuration> <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> <encoder> <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern> </encoder> </appender> <root level="INFO"> <appender-ref ref="STDOUT"/> </root> </configuration>

Hau gure logs konfigurazio oso erraza da. Azkenik, agian gure aplikazioko fitxategirik garrantzitsuena. Hau konfigurazio-txantiloia da. Une honetan, kontuan izan behar da proiektu honetan sortu ditudan fitxategi batzuk txantiloi-egitura baten parte direla. Geroago azalduko dut horri buruz. Txantiloi-fitxategi hau MicroProfile-k irakurriko duen config.yml fitxategi batean bihurtu behar da. Fitxategi hau baliabideen erroan dago:

 kumuluzee: name: your-financeje-banking version: 1.0.0 jwt-auth: public-key: {{ publicKey }} issuer: {{ issuer }} healthy: true

Aurrerago ikusiko dugu propietate horiek guztiek benetan zer esan nahi duten. Horiek guztiak berez azaltzen dira. PublicKey eta jaulkitzailea ordezkatuko diren parametro guztiak dira. Hori geroago aztertuko dugu. Gure bash script-ek ordezkatuko dutela ziurtatuko dute. Ia prest gaude kodetzeko, baina lehenik eta behin, ikus dezagun gure JWT token-egitura.

6. Esku-kodea

Egin dezagun gure aplikazio txikia. Atal honetan azalduko da nola lor dezakegun gure aplikazioa JWT rekin funtziona dezan. Ikusi nahi duguna da erabiltzaileak zehaztu ditzakegun gure REST metodo batzuetara sartzeko eta ez beste batzuk. Kode hau aztertzen hasteko moduetako bat da lehenik eta behin gure JWT token arruntari begirada bat ematea. Hona hemen gure administratzailearen adibidea:

 { "iss": "joaofilipesabinoesperancinha", "jti": "01MASTERFINANCE", "sub": "admin", "aud": "nature", "upn": "admin", "groups": [ "user", "admin", "client", "credit" ], "user_id": 1, "access": "TOP", "name": "Admin" }

Gure JSON ko izen hauetako bakoitza erreklamazio gisa deitzen da. Gure adibidean, Erreserbatutako erreklamazio batzuk ikusten ditugu:

  • " iss " — Hau da tokenaren igorlea. Arbitrioan aukera dezakegu horretarako balio bat. Parametro honen balioak lehen ikusi dugun konfig.yml-n ordezkatu beharreko jaulkitzailearen aldagaiarekin bat etorri behar du.
  • " jti " — Hau tokenaren identifikatzaile bakarra da. Adibidez, erreklamazio hau erabil dezakegu token bat bi aldiz edo gehiagotan erabiltzea saihesteko.
  • " sub " — Hau da tokenaren gaia. Erabiltzailea edo gustuko dugun edozer izan daiteke. Kontuan izan behar da hori identifikatzaile, gako, izendapen edo nahi dugun edozer gisa ere erabil daitekeela.
  • " upn " — Erabiltzaile nagusiaren izena. Erabiltzaileak erabiltzen ari den nagusia identifikatzeko erabiltzen da.
  • " groups " — Uneko erabiltzailea dagoen taldeen multzoa da. Funtsean, horrek zehaztuko du token honekin eskaera batek zer egin dezakeen. Gure tokenean, erreklamazio pertsonalizatu batzuk ikusiko ditugu. Hau erreserbatutako erreklamazioak bezain ondo erabil dezakegu
  • " user_id " — Hau erabiliko dugu erabiltzailearen IDa ezartzeko.
  • " access " — Erabiltzailearen sarbide-maila zehaztuko dugu.
  • " name " — Erabiltzailearen izena.

7. Esku-kodea

Egin dezagun orain arte dakigunaren laburpena. Badakigu guk zehaztutako egitura duten tokenekin komunikatuko garela. Gainera, gure aplikazioaren konfigurazioa, logback konfigurazioa eta azkenik, enpresa babarrunaren bilaketarako konfigurazio pertsonalizatua ezarri dugu. Ikus dezagun pakete eredua. Hemen 3 klase aurkituko ditugu. Klase hauek, funtsean, kontuen agregazioa eta client eta account arteko ordezkaritza besterik ez dute adierazten. Horrela hasi gaitezke kotlin fitxategia Model.kt non dagoen Client kokatuta:

 data class Client constructor( @JsonProperty var name: String ?= null )

Lehen eredu-klase hau gure bezeroaren irudikapena da. Gure kasurako gure client izen bat besterik ez du. Hau da " jwt " atributuaren izenak adierazten duen erabiltzaile-izena. Gainera, Account dugu:

 data class Account( @JsonProperty val accountNumber: String?, @JsonProperty val client: Client? = null, @JsonProperty var currentValue: BigDecimal = BigDecimal.ZERO, @JsonProperty var creditValue: BigDecimal = BigDecimal.ZERO ) { fun addCurrentValue(value: Long) = Account( accountNumber, client, currentValue .add(BigDecimal.valueOf(value)), creditValue ) fun addCreditValue(value: Long): Account = Account( accountNumber, client, currentValue, currentValue .add(BigDecimal.valueOf(value)) ) }


Klase honetan, funtsean, kontu-zenbaki bat, bezero bat, uneko balio bat eta azkenik, kreditu-balio bat konfiguratu ditugu. Kontuan izan balio guztiak 0-ra ezartzen ari garela. BigDecimal ere erabiltzen ari gara, diruarekin ari garelako soilik. Diruak zehatza izan behar du eta ezin du jasan sistema biribilketa edo biribilketarik. Horrek esan nahi du eta adibide gisa 0. 0000000000000000000000000000000000000000000000000001 euro zenbaki hori denbora guztian mantendu behar duela. Gainera, gure kontuari balioak gehitu nahi dizkiogu. Hortxe sortzen da addCurrentValue metodoa. Arrazoi berberengatik, gure kreditua ere gehituko dugu addCreditValue . Azkenik, gure datuen konfigurazioaren azken zatian klaseko Accounts topatuko ditugu:

 open class Accounts constructor( open val accountMap: MutableMap<String, Account> = mutableMapOf() )

Hau funtsean gure kontu guztien agregatzaile bat besterik ez da. Bere maparen edukia datu-base baten portaera imitatzeko erabiliko dugu. Orain ikus dezagun kontrolagailu paketea. Hemen sortzen dugu gure aplikazioa gure datu-ereduarekin martxan. Lehenik eta behin, ikus dezagun BankApplication klaseari:

 @LoginConfig(authMethod = "MP-JWT") @ApplicationPath("/") @DeclareRoles("admin", "creditor", "client", "user") class BankApplication : Application()


Honekin, 3 gauza garrantzitsu esaten ari gara. LoginConfig oharpenarekin, MicroProfile-ren arabera JWT tokenak erabiltzeko eta ulertzeko definitzen dugu. ApplicationPath-ek aplikazioaren erroa definitzen du. Hemen hasiko da aplikazioaren URLa. Gure adibidean, HTTP://localhost:8080 izango da. Azkenik, DeclareRoles-ek gure aplikazioak erabiliko eta onartuko dituen rolak definitzen ditu. Rolak eta Taldeak termino trukagarriak dira egoera honetan. Injekzioak modu eraginkorrean funtziona dezan, kontu-mapa identifikatzeko berariazko oharpen bat sortzen dugu:

 annotation class AccountsProduct

Sartu pantaila osoko moduan Irten pantaila osoko moduan

Ondoren, cache-objektuen fabrika AccountsFactory sortuko dugu:

 class AccountsFactory : Serializable { @Produces @AccountsProduct @ApplicationScoped fun accounts(): Accounts = Accounts(mutableMapOf()) companion object { @Throws(JsonProcessingException::class) fun createResponse( currentAccount: Account, name: JsonString, accounts: Accounts, log: Logger, objectMapper: ObjectMapper, principal: Principal?, jsonWebToken: JsonWebToken? ): Response { val jsonObject = Json.createObjectBuilder() .add("balance", currentAccount.currentValue) .add("client", name) .build() accounts.accountMap[name.string] = currentAccount log.info("Principal: {}", objectMapper.writeValueAsString(principal)) log.info("JSonWebToken: {}", objectMapper.writeValueAsString(jsonWebToken)) return Response.ok(jsonObject) .build() } } }


Fabrika hau da Accounts bereziki bilaketa desgaitu dugulako. Bilaketa-prozesuari babarrun bat sortzeko baimena eman beharrean, agregatzaile-instantzia geuk sortzen dugu. Produces anotazioa erabiliz, baba sortzeko aukera ematen digu. Gure oharpen pertsonalizatua erabiliz, AccountsProduct, babarrun honen erabilera zehatzagoa egiten dugu. Azkenik, ApplicationScoped erabiliz, bere esparrua Application esparrua dela definitzen dugu. Beste era batera esanda, kontuaren agregazio babarrunak aplikazioan zehar singleton objektu gisa jokatuko du. " createResponse " JSON erantzunak sortzeko metodo generiko bat besterik ez da. Orain behar ditugunak bi "Baliabide" dira. Funtsean, udaberriko " Controllers "-en berdina da. Beste izen bat da, baina erabilera berdina du. Ikus dezagun AccountsResource klasea:

 @Path("accounts") @RequestScoped @Produces(MediaType.APPLICATION_JSON) open class AccountResource { @Inject @AccountsProduct open var accounts: Accounts? = null @Inject open var principal: Principal? = null @Inject open var jsonWebToken: JsonWebToken? = null @Inject @Claim("access") open var access: JsonString? = null @Claim("iat") @Inject open var iat: JsonNumber? = null @Inject @Claim("name") open var name: JsonString? = null @Inject @Claim("user_id") open var userId: JsonNumber? = null @POST @RolesAllowed("admin", "client", "credit") @Throws(JsonProcessingException::class) open fun createAccount(): Response = createResponse( requireNotNull(accounts).accountMap[requireNotNull(name).string] ?: Account( client = Client(name = requireNotNull(name).string), accountNumber = UUID.randomUUID().toString() ) ) @POST @RolesAllowed("admin", "user") @Path("user") @Throws(JsonProcessingException::class) open fun createUser(): Response { return createResponse( requireNotNull(accounts).accountMap[requireNotNull(name).string] ?: Account( client = Client(name = requireNotNull(name).string), accountNumber = UUID.randomUUID().toString() ) ) } @GET @RolesAllowed("admin", "client") @Throws(JsonProcessingException::class) open fun getAccount(): Response? { return createResponse( requireNotNull(accounts).accountMap[requireNotNull(name).string] ?: return Response.serverError() .build() ) } @PUT @RolesAllowed("admin", "client") @Consumes(MediaType.APPLICATION_JSON) @Throws( JsonProcessingException::class ) open fun cashIn(transactionBody: TransactionBody): Response? { val userAccount = requireNotNull(accounts).accountMap[requireNotNull(name).string] ?: return Response.serverError() .build() val currentAccount = userAccount.addCurrentValue(transactionBody.saldo?: 0) requireNotNull(accounts).accountMap[requireNotNull(name).string] = currentAccount return createResponse(currentAccount) } @GET @Path("all") @Produces(MediaType.APPLICATION_JSON) @Throws( JsonProcessingException::class ) open fun getAll(): Response? { val allAccounts = ArrayList( requireNotNull(accounts).accountMap .values ) logger.info("Principal: {}", objectMapper.writeValueAsString(principal)) logger.info("JSonWebToken: {}", objectMapper.writeValueAsString(jsonWebToken)) return Response.ok(allAccounts) .build() } @GET @Path("summary") @Throws(JsonProcessingException::class) open fun getSummary(): Response? { val totalCredit = requireNotNull(accounts).accountMap .values .map(Account::currentValue) .stream() .reduce { result, u -> result.add(u) } .orElse(BigDecimal.ZERO) val jsonObject = Json.createObjectBuilder() .add("totalCurrent", totalCredit) .add("client", "Mother Nature Dream Team") .build() logger.info("Summary") logger.info("Principal: {}", objectMapper.writeValueAsString(principal)) logger.info("JSonWebToken: {}", objectMapper.writeValueAsString(jsonWebToken)) return Response.ok(jsonObject) .build() } @GET @RolesAllowed("admin", "client") @Path("jwt") open fun getJWT(): Response? { val jsonObject = Json.createObjectBuilder() .add("jwt", requireNotNull(jsonWebToken).rawToken) .add("userId", requireNotNull(userId).doubleValue()) .add("access", requireNotNull(access).string) .add("iat", requireNotNull(iat).doubleValue()) .build() return Response.ok(jsonObject) .build() } @Throws(JsonProcessingException::class) private fun createResponse(currentAccount: Account): Response = AccountsFactory.createResponse( currentAccount, requireNotNull(name), requireNotNull(accounts), logger, objectMapper, principal, jsonWebToken ) companion object { val objectMapper: ObjectMapper = ObjectMapper() val logger: Logger = LoggerFactory.getLogger(AccountResource::class.java) } }

Hartu une bat klase hau zehatzago ikusteko. Path oharrak baliabide honetara errotik nola iritsi definitzen du. Gogoratu "/" erro gisa erabiltzen ari garela. Kasu honetan, "kontuak" da baliabide honetarako gure root sarbide-puntua. Gure baliabide guztiak, gure kasuan bi bakarrik ari dira exekutatzen RequestResource esparruarekin. Oharpenarekin Produces-ek zehazten du edozein motatako eskaera guztien erantzun guztiek JSON formatuko mezuen forma hartuko dutela. Gure aggregator injektatzeko, Inject annotation eta AccountsProduct oharpenaren konbinazioa besterik ez dugu erabiltzen:

 @Inject @AccountsProduct open var accounts: Accounts? = null


Hau fabrikan definitu genuenarekin bat dator. Gainera, bi segurtasun elementu garrantzitsu ere sartzen ari gara. principal bat eta jsonWebToken :

 @Inject open var principal: Principal? = null @Inject open var jsonWebToken: JsonWebToken? = null


Biak JsonWebToken eta Principal berdinak izango dira, eta hori gure erregistroetan ikusiko dugu. Gure baliabideetan, eskaera baten erreklamazioak beti injektatu ditzakegu token jakin batekin:

 @Inject @Claim("name") open var name: JsonString? = null @Inject @Claim("user_id") open var userId: JsonNumber? = null


Hau Inject eta Claim oharpenen konbinazioarekin lortzen da. Claim oharpenaren azpian jarritako izenak definitzen du zein erreklamazio sartu nahi dugun. Kontuz ibili behar dugu gure parametroak definitzen ditugun motarekin. Gure adibidean,r JsonString eta JsonNumber motak bakarrik behar ditugu. Lehenik eta behin, ikus dezagun nola sortzen ari garen kontuak eta erabiltzaileak:

 @POST @RolesAllowed("admin", "client", "credit") @Throws(JsonProcessingException::class) open fun createAccount(): Response = createResponse( requireNotNull(accounts).accountMap[requireNotNull(name).string] ?: Account( client = Client(name = requireNotNull(name).string), accountNumber = UUID.randomUUID().toString() ) ) @POST @RolesAllowed("admin", "user") @Path("user") @Throws(JsonProcessingException::class) open fun createUser(): Response { return createResponse( requireNotNull(accounts).accountMap[requireNotNull(name).string] ?: Account( client = Client(name = requireNotNull(name).string), accountNumber = UUID.randomUUID().toString() ) ) }

Kontuak eta erabiltzaileak sortzea


Hemen helburua metodoak bereiztea eta baimen desberdinak ematea da. Gure adibidean, biek kontu bat sortzen dute, baina garrantzitsua da konturatzea erabiltzaile rolak dituzten erabiltzaileek soilik erabil dezaketela createUser metodoa. Modu berean, bezero eta kreditu rolak dituzten erabiltzaileek soilik atzi dezakete createAccount metodora. Ikus dezagun orain xehetasunez baliabide honen PUT eskaera metodoa:

 @PUT @RolesAllowed("admin", "client") @Consumes(MediaType.APPLICATION_JSON) @Throws( JsonProcessingException::class ) open fun cashIn(transactionBody: TransactionBody): Response? { val userAccount = requireNotNull(accounts).accountMap[requireNotNull(name).string] ?: return Response.serverError() .build() val currentAccount = userAccount.addCurrentValue(transactionBody.saldo?: 0) requireNotNull(accounts).accountMap[requireNotNull(name).string] = currentAccount return createResponse(currentAccount) }

Kobratzea


Badakigu PUT oharrak adierazten duela metodo hau PUT motako eskaerekin soilik eskura daitekeela. Annotation Path-ek metodo honetarako bidea balio bat dela esaten dio Jettyri. Hau PathParam gisa ere ezagutzen da. Azkenik, metodo hau administratzaile edo bezero rolak dituzten erabiltzaileek bakarrik erabiltzeko baimena defini dezakegu. Sarrerako balioa PathParam-en erabileraren bidez gure balio luzeko aldagaira pasatzen da. Ez badugu rolik definitzen, token egokia duen edozein erabiltzailek metodo horietara sartzeko aukera izango du. CreditResource berean inplementatzen da. modua:

 @Path("credit") @RequestScoped @Produces(MediaType.APPLICATION_JSON) open class CreditResource { @Inject @AccountsProduct open var accounts: Accounts? = null @Inject open var principal: Principal? = null @Inject open var jsonWebToken: JsonWebToken? = null @Inject @Claim("access") open var access: JsonString? = null @Inject @Claim("iat") open var iat: JsonNumber? = null @Inject @Claim("name") open var name: JsonString? = null @Inject @Claim("user_id") open var userId: JsonNumber? = null @GET @RolesAllowed("admin", "credit") @Throws(JsonProcessingException::class) open fun getAccount(): Response = requireNotNull(accounts).let { accounts -> createResponse( accounts.accountMap[requireNotNull(name).string] ?: return Response.serverError().build() ) } @PUT @RolesAllowed("admin", "credit") @Consumes(MediaType.APPLICATION_JSON) @Throws( JsonProcessingException::class ) open fun cashIn(transactionBody: TransactionBody) = requireNotNull(accounts).let { accounts -> requireNotNull(name).let { name -> accounts.accountMap[name.string] = (accounts.accountMap[name.string] ?: return Response.serverError() .build()).addCreditValue(transactionBody.saldo?: 0L) createResponse( (accounts.accountMap[name.string] ?: return Response.serverError() .build()).addCreditValue(transactionBody.saldo?: 0L) ) } } @GET @Path("all") @Produces(MediaType.APPLICATION_JSON) @Throws( JsonProcessingException::class ) open fun getAll(): Response? { val allAccounts = ArrayList( requireNotNull(accounts).accountMap .values ) logger.info("Principal: {}", objectMapper.writeValueAsString(principal)) logger.info("JSonWebToken: {}", objectMapper.writeValueAsString(jsonWebToken)) return Response.ok(allAccounts) .build() } @GET @Path("summary") @Produces(MediaType.APPLICATION_JSON) @Throws( JsonProcessingException::class ) open fun getSummary(): Response? { val totalCredit = requireNotNull(accounts).accountMap .values .map(Account::creditValue) .stream() .reduce { total, v -> total.add(v) } .orElse(BigDecimal.ZERO) val jsonObject = Json.createObjectBuilder() .add("totalCredit", totalCredit) .add("client", "Mother Nature Dream Team") .build() logger.info("Summary") logger.info("Principal: {}", objectMapper.writeValueAsString(principal)) logger.info("JSonWebToken: {}", objectMapper.writeValueAsString(jsonWebToken)) return Response.ok(jsonObject) .build() } @GET @RolesAllowed("admin", "client") @Path("jwt") open fun getJWT(): Response? { val jsonObject = Json.createObjectBuilder() .add("jwt", requireNotNull(jsonWebToken).rawToken) .add("userId", requireNotNull(userId).doubleValue()) .add("access", requireNotNull(access).string) .add("iat", requireNotNull(iat).doubleValue()) .build() return Response.ok(jsonObject) .build() } @Throws(JsonProcessingException::class) private fun createResponse(currentAccount: Account): Response { return AccountsFactory.createResponse( currentAccount, requireNotNull(name), requireNotNull(accounts), logger, objectMapper, principal, jsonWebToken ) } companion object { val objectMapper: ObjectMapper = ObjectMapper() val logger: Logger = LoggerFactory.getLogger(CreditResource::class.java) } }

Desberdintasun bakarra da admin eta client rolak erabili beharrean admin eta credit rolak erabiltzen ari garela. Gainera, konturatu resource honetan erabiltzaileentzako kontuak ez direla inoiz sortuko. Hori kontuaren resource bidez bakarrik da posible. Orain, kodea nola inplementatzen den dakigunez, labur dezagun lehenik eta behin zein metodo jarri ditugun eskuragarri gure REST zerbitzuan.

8. Aplikazioaren erabilera

Ikus dezagun erabiltzen ari diren zerbitzuen zerrenda:


Mota,URLa,Payload,Emaitza,Baimendutako rolak
POST, http://localhost:8080/accounts,n/a,Sortua kontua, administratzailea/bezeroa/kreditua
POST,
http://localhost:8080/accounts/user,n/a,Sortua erabiltzailea,administratzailea/erabiltzailea
LORTU,
http://localhost:8080/accounts,n/a,Matching kontua, administratzailea/bezeroa
JARRI,
http://localhost:8080/accounts,{saldo: Long}, Uneko saldoa, administratzailea/bezeroa
LORTU,
http://localhost:8080/accounts/all,n/a,All kontu korronteak,Guztiak
LORTU,
http://localhost:8080/accounts/summary,n/a,Sum saldo guztiena,Guztiak
LORTU,
http://localhost:8080/credit,n/a,Matching kontua, administratzailea/bezeroa
JARRI,
http://localhost:8080/credit,{saldo: Long}, uneko kreditua, administratzailea/bezeroa
LORTU,
http://localhost:8080/credit/all,n/a,All Kredituak, Guztiak
LORTU,
http://localhost:8080/credit/summary,n/a,Sum
Kredituak, Guztiak

9. Proba-ingurunea sortzea

bash fitxategi bat sortu dut erro karpetan. Fitxategi hau "setupCertificates.sh" deitzen da. Ikus dezagun zer egiten duen ideia bat izateko:

 #!/bin/bash mkdir -p your-finance-files cd your-finance-files || exit openssl genrsa -out baseKey.pem openssl pkcs8 -topk8 -inform PEM -in baseKey.pem -out privateKey.pem -nocrypt openssl rsa -in baseKey.pem -pubout -outform PEM -out publicKey.pem echo -e '\033[1;32mFirst test\033[0m' java -jar ../your-finance-jwt-generator/target/your-finance-jwt-generator.jar \ -p ../jwt-plain-tokens/jwt-token-admin.json \ -key ../your-finance-files/privateKey.pem >> token.jwt CERT_PUBLIC_KEY=$(cat ../your-finance-files/publicKey.pem) CERT_ISSUER="joaofilipesabinoesperancinha" echo -e "\e[96mGenerated public key: \e[0m $CERT_PUBLIC_KEY" echo -e "\e[96mIssued by: \e[0m $CERT_ISSUER" echo -e "\e[96mYour token is: \e[0m $(cat token.jwt)" cp ../your-financeje-banking/src/main/resources/config-template ../your-financeje-banking/src/main/resources/config_copy.yml CERT_CLEAN0=${CERT_PUBLIC_KEY//"/"/"\/"} CERT_CLEAN1=${CERT_CLEAN0//$'\r\n'/} CERT_CLEAN2=${CERT_CLEAN1//$'\n'/} CERT_CLEAN3=$(echo "$CERT_CLEAN2" | awk '{gsub("-----BEGIN PUBLIC KEY-----",""); print}') CERT_CLEAN4=$(echo "$CERT_CLEAN3" | awk '{gsub("-----END PUBLIC KEY-----",""); print}') CERT_CLEAN=${CERT_CLEAN4//$' '/} echo -e "\e[96mCertificate cleanup: \e[0m ${CERT_CLEAN/$'\n'/}" sed "s/{{ publicKey }}/$CERT_CLEAN/g" ../your-financeje-banking/src/main/resources/config_copy.yml > ../your-financeje-banking/src/main/resources/config_cert.yml sed "s/{{ issuer }}/$CERT_ISSUER/g" ../your-financeje-banking/src/main/resources/config_cert.yml > ../your-financeje-banking/src/main/resources/config.yml rm ../your-financeje-banking/src/main/resources/config_cert.yml rm ../your-financeje-banking/src/main/resources/config_copy.yml echo -e "\e[93mSecurity elements completely generated!\e[0m" echo -e "\e[93mGenerating tokens...\e[0m" TOKEN_FOLDER=jwt-tokens mkdir -p ${TOKEN_FOLDER} # CREATE_ACCOUNT_FILE=createAccount.sh CREATE_USER_FILE=createUser.sh SEND_MONEY_FILE=sendMoney.sh ASK_CREDIT_FILE=askCredit.sh TOKEN_NAME_VALUE=tokenNameValue.csv echo "#!/usr/bin/env bash" > ${CREATE_ACCOUNT_FILE} chmod +x ${CREATE_ACCOUNT_FILE} echo "#!/usr/bin/env bash" > ${CREATE_USER_FILE} chmod +x ${CREATE_USER_FILE} echo "#!/usr/bin/env bash" > ${SEND_MONEY_FILE} chmod +x ${SEND_MONEY_FILE} echo "#!/usr/bin/env bash" > ${ASK_CREDIT_FILE} chmod +x ${ASK_CREDIT_FILE} for item in ../jwt-plain-tokens/jwt-token*.json; do if [[ -f "$item" ]]; then filename=${item##*/} per_token=${filename/jwt-token-/} token_name=${per_token/.json/} cp "${item}" jwt-token.json java -jar ../your-finance-jwt-generator/target/your-finance-jwt-generator.jar \ -p jwt-token.json \ -key ../your-finance-files/privateKey.pem > token.jwt cp token.jwt ${TOKEN_FOLDER}/token-"${token_name}".jwt token=$(cat token.jwt) echo "# Create account: ""${token_name}" >> ${CREATE_ACCOUNT_FILE} echo "echo -e \"\e[93mCreating account \e[96m${token_name}\e[0m\"" >> ${CREATE_ACCOUNT_FILE} echo curl -i -H"'Authorization: Bearer ""${token}""'" http://localhost:8080/accounts -X POST >> ${CREATE_ACCOUNT_FILE} echo "echo -e \"\e[93m\n---\e[0m\"" >> ${CREATE_ACCOUNT_FILE} echo "# Create user: ""${token_name}" >> ${CREATE_USER_FILE} echo "echo -e \"\e[93mCreating user \e[96m${token_name}\e[0m\"" >> ${CREATE_USER_FILE} echo curl -i -H"'Authorization: Bearer ""${token}""'" http://localhost:8080/accounts/user -X POST >> ${CREATE_USER_FILE} echo "echo -e \"\e[93m\n---\e[0m\"" >> ${CREATE_USER_FILE} echo "# Send money to: "${token_name} >> ${SEND_MONEY_FILE} echo "echo -e \"\e[93mSending money to \e[96m${token_name}\e[0m\"" >> ${SEND_MONEY_FILE} echo curl -i -H"'Content-Type: application/json'" -H"'Authorization: Bearer ""${token}""'" http://localhost:8080/accounts -X PUT -d "'{ \"saldo\": "$((1 + RANDOM % 500))"}'" >> ${SEND_MONEY_FILE} echo "echo -e \"\e[93m\n---\e[0m\"" >> ${SEND_MONEY_FILE} echo "# Asking money credit to: "${token_name} >> ${ASK_CREDIT_FILE} echo "echo -e \"\e[93mAsking credit from \e[96m${token_name}\e[0m\"" >> ${ASK_CREDIT_FILE} echo curl -i -H"'Content-Type: application/json'" -H"'Authorization: Bearer ""${token}""'" http://localhost:8080/credit -X PUT -d "'{ \"saldo\": "$((1 + RANDOM % 500))"}'">> ${ASK_CREDIT_FILE} echo "echo -e \"\e[93m\n---\e[0m\"" >> ${ASK_CREDIT_FILE} echo "${token_name},${token}" >> ${TOKEN_NAME_VALUE} fi done

Ingurumenaren sorrera

Mesedez, jarraitu fitxategia zer egiten duen azaltzen dudan bitartean. Hau garrantzitsua da, zertan ari den zehazki ulertzeko. Lehenik eta behin gako pribatuak eta publikoak sortzen ditugu PEM formatuan. Ondoren, gako pribatua erabiltzen dugu gure "your-finance-jwt-generator.jar" exekutatu daitekeenarekin. Hau exekutatu daitekeen gure potoa da, tokenak azkar sortzeko aukera ematen duena. Jaulkitzailea ezin da geroago aldatu. Azkenik, token bat sortzen du. Aurrerago ikusiko dugu token hau nola irakurri. Token honek 3 goiburuko erreklamazio gehigarri ditu. Hauek "kid", "typ" eta "alg" dira. Formatu hau jarraitzen du:

 { "kid": "jwt.key", "typ": "JWT", "alg": "RS256" }

JWT goiburua

Ikus ditzagun erreklamazio hauek hurbilagotik:

  • "kid" - iradokizun erreklamazio gisa funtzionatzen du. Zein algoritmo mota erabiltzen ari garen adierazten du.
  • "typ" — IANA euskarri motak deklaratzeko erabiltzen da. Hiru aukera daude JWT (JSON Web Token), JWE (JSON Web Encryption) eta JWA (JSON Web Algorithms). Mota hauek ez dira garrantzitsuak gure esperimenturako. Ikusiko dugu gure tokena ez dagoela oso ondo enkriptatuta eta oso erraza dela deszifratzea. Era berean, ikusiko dugu tokenak deszifratu ditzakegun arren, ezin ditugula erraz manipulatu beste ekintza batzuk egiteko.
  • "alg" — Horrela definitzen dugu erabili nahi dugun sinadura mota. Sinatzea jatorrizko tokena aldatu ez dela eta fidagarria dela bermatuko duen eragiketa kriptografikotzat har daiteke. Gure kasuan, RS256 erabiltzen ari gara, bestela RSA sinadura bezala ezagutzen dena SHA-256-rekin.

Gure gako publikoarekin, azkenean erabil dezakegu gure txantiloia aldatzeko. Config.yml fitxategi berriak honelako itxura izan beharko luke:

 kumuluzee: name: your-financeje-banking version: 1.0.0 jwt-auth: public-key: FAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKE.FAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETO.FAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKEN issuer: joaofilipesabinoesperancinha healthy: true

konfig.yml


Bigarren urratsa lau fitxategi sortzea da. " jwt-plain-tokens " direktorioko token arrunt bakoitzeko, lau komando sortuko ditugu. Lehenengo komandoa beren kontuekin gauzak eraginkortasunez egin ditzaketen erabiltzaileak sortzea da. Hauek " admin ", " client " eta " credit " profilak dituzten erabiltzaileak dira. Exekutatu dezagun " createAccount.sh " fitxategia, horiek sortzeko. Bigarren komandoak oraindik eskubiderik ez duten gainerako erabiltzaileak sortuko ditu. Hau "createUser.sh" fitxategia da. Exekutatu dezagun. Orain ikusiko dugu azkenean erabiltzaile guztiak sortu direla. Ikus ditzagun orain transakzioei buruzko xehetasunak eta ikus ditzagun gainerako bi komandoak. Bat "cashin" eta beste bat kreditu gehiago eskatzeko. Sortutako lehen fitxategia "sendMoney.sh" bash script-a da. Hemen " cashin "-i egindako eskaera guztiak aurki ditzakegu. Fitxategi honetan erabiltzaileei ausazko diru kantitateak bidaltzeko kizkur eskaera bat aurkituko duzu, erabiltzaile bakoitzeko. Ikus dezagun administratzailearen kasua:

 #!/usr/bin/env bash # Send money to: admin echo -e "\e[93mSending money to \e[96madmin\e[0m" curl -i -H'Content-Type: application/json' -H'Authorization: Bearer= FAKE.FAKE.FAKE' http://localhost:8080/accounts -X PUT -d '{ "saldo": 125}' echo -e "\e[93m\n---\e[0m" # Send money to: cindy echo -e "\e[93mSending money to \e[96mcindy\e[0m" curl -i -H'Content-Type: application/json' -H'Authorization: Bearer FAKE.FAKE.FAKE' http://localhost:8080/accounts -X PUT -d '{ "saldo": 360}' echo -e "\e[93m\n---\e[0m" # Send money to: faustina echo -e "\e[93mSending money to \e[96mfaustina\e[0m" curl -i -H'Content-Type: application/json' -H'Authorization: Bearer FAKE.FAKE.FAKE' http://localhost:8080/accounts -X PUT -d '{ "saldo": 50}' echo -e "\e[93m\n---\e[0m" # Send money to: jack echo -e "\e[93mSending money to \e[96mjack\e[0m" curl -i -H'Content-Type: application/json' -H'Authorization: Bearer FAKE.FAKE.FAKE' http://localhost:8080/accounts -X PUT -d '{ "saldo": 205}' echo -e "\e[93m\n---\e[0m" # Send money to: jitska echo -e "\e[93mSending money to \e[96mjitska\e[0m" curl -i -H'Content-Type: application/json' -H'Authorization: Bearer FAKE.FAKE.FAKE' http://localhost:8080/accounts -X PUT -d '{ "saldo": 332}' echo -e "\e[93m\n---\e[0m" # Send money to: judy echo -e "\e[93mSending money to \e[96mjudy\e[0m" curl -i -H'Content-Type: application/json' -H'Authorization: Bearer FAKE.FAKE.FAKE' http://localhost:8080/accounts -X PUT -d '{ "saldo": 295}' echo -e "\e[93m\n---\e[0m" # Send money to: lucy echo -e "\e[93mSending money to \e[96mlucy\e[0m" curl -i -H'Content-Type: application/json' -H'Authorization: Bearer FAKE.FAKE.FAKE' http://localhost:8080/accounts -X PUT -d '{ "saldo": 160}' echo -e "\e[93m\n---\e[0m" # Send money to: malory echo -e "\e[93mSending money to \e[96mmalory\e[0m" curl -i -H'Content-Type: application/json' -H'Authorization: Bearer FAKE.FAKE.FAKE' http://localhost:8080/accounts -X PUT -d '{ "saldo": 413}' echo -e "\e[93m\n---\e[0m" # Send money to: mara echo -e "\e[93mSending money to \e[96mmara\e[0m" curl -i -H'Content-Type: application/json' -H'Authorization: Bearer FAKE.FAKE.FAKE' http://localhost:8080/accounts -X PUT -d '{ "saldo": 464}' echo -e "\e[93m\n---\e[0m" # Send money to: namita echo -e "\e[93mSending money to \e[96mnamita\e[0m" curl -i -H'Content-Type: application/json' -H'Authorization: Bearer FAKE.FAKE.FAKE' http://localhost:8080/accounts -X PUT -d '{ "saldo": 51}' echo -e "\e[93m\n---\e[0m" # Send money to: pietro echo -e "\e[93mSending money to \e[96mpietro\e[0m" curl -i -H'Content-Type: application/json' -H'Authorization: Bearer FAKE.FAKE.FAKE' http://localhost:8080/accounts -X PUT -d '{ "saldo": 491}' echo -e "\e[93m\n---\e[0m" # Send money to: rachelle echo -e "\e[93mSending money to \e[96mrachelle\e[0m" curl -i -H'Content-Type: application/json' -H'Authorization: Bearer FAKE.FAKE.FAKE' http://localhost:8080/accounts -X PUT -d '{ "saldo": 474}' echo -e "\e[93m\n---\e[0m" # Send money to: sandra echo -e "\e[93mSending money to \e[96msandra\e[0m" curl -i -H'Content-Type: application/json' -H'Authorization: Bearer FAKE.FAKE.FAKE' http://localhost:8080/accounts -X PUT -d '{ "saldo": 417}' echo -e "\e[93m\n---\e[0m" # Send money to: shikka echo -e "\e[93mSending money to \e[96mshikka\e[0m" curl -i -H'Content-Type: application/json' -H'Authorization: Bearer FAKE.FAKE.FAKE' http://localhost:8080/accounts -X PUT -d '{ "saldo": 64}' echo -e "\e[93m\n---\e[0m"

sendMoney.sh laburpena

Erabiltzaile berdinei kreditu-eskaerak ere esleitu zaizkie:

 #!/usr/bin/env bash # Asking money credit to: admin echo -e "\e[93mAsking credit from \e[96madmin\e[0m" curl -i -H'Content-Type: application/json' -H'Authorization: Bearer FAKE.FAKE.FAKE' http://localhost:8080/credit -X PUT -d '{ "saldo": 137}' echo -e "\e[93m\n---\e[0m" # Asking money credit to: cindy echo -e "\e[93mAsking credit from \e[96mcindy\e[0m" curl -i -H'Content-Type: application/json' -H'Authorization: Bearer FAKE.FAKE.FAKE' http://localhost:8080/credit -X PUT -d '{ "saldo": 117}' echo -e "\e[93m\n---\e[0m" # Asking money credit to: faustina echo -e "\e[93mAsking credit from \e[96mfaustina\e[0m" curl -i -H'Content-Type: application/json' -H'Authorization: Bearer FAKE.FAKE.FAKE' http://localhost:8080/credit -X PUT -d '{ "saldo": 217}' echo -e "\e[93m\n---\e[0m" # Asking money credit to: jack echo -e "\e[93mAsking credit from \e[96mjack\e[0m" curl -i -H'Content-Type: application/json' -H'Authorization: Bearer FAKE.FAKE.FAKE' http://localhost:8080/credit -X PUT -d '{ "saldo": 291}' echo -e "\e[93m\n---\e[0m" # Asking money credit to: jitska echo -e "\e[93mAsking credit from \e[96mjitska\e[0m" curl -i -H'Content-Type: application/json' -H'Authorization: Bearer FAKE.FAKE.FAKE' http://localhost:8080/credit -X PUT -d '{ "saldo": 184}' echo -e "\e[93m\n---\e[0m" # Asking money credit to: judy echo -e "\e[93mAsking credit from \e[96mjudy\e[0m" curl -i -H'Content-Type: application/json' -H'Authorization: Bearer FAKE.FAKE.FAKE' http://localhost:8080/credit -X PUT -d '{ "saldo": 388}' echo -e "\e[93m\n---\e[0m" # Asking money credit to: lucy echo -e "\e[93mAsking credit from \e[96mlucy\e[0m" curl -i -H'Content-Type: application/json' -H'Authorization: Bearer FAKE.FAKE.FAKE' http://localhost:8080/credit -X PUT -d '{ "saldo": 219}' echo -e "\e[93m\n---\e[0m" # Asking money credit to: malory echo -e "\e[93mAsking credit from \e[96mmalory\e[0m" curl -i -H'Content-Type: application/json' -H'Authorization: Bearer FAKE.FAKE.FAKE' http://localhost:8080/credit -X PUT -d '{ "saldo": 66}' echo -e "\e[93m\n---\e[0m" # Asking money credit to: mara echo -e "\e[93mAsking credit from \e[96mmara\e[0m" curl -i -H'Content-Type: application/json' -H'Authorization: Bearer FAKE.FAKE.FAKE' http://localhost:8080/credit -X PUT -d '{ "saldo": 441}' echo -e "\e[93m\n---\e[0m" # Asking money credit to: namita echo -e "\e[93mAsking credit from \e[96mnamita\e[0m" curl -i -H'Content-Type: application/json' -H'Authorization: Bearer FAKE.FAKE.FAKE' http://localhost:8080/credit -X PUT -d '{ "saldo": 358}' echo -e "\e[93m\n---\e[0m" # Asking money credit to: pietro echo -e "\e[93mAsking credit from \e[96mpietro\e[0m" curl -i -H'Content-Type: application/json' -H'Authorization: Bearer FAKE.FAKE.FAKE' http://localhost:8080/credit -X PUT -d '{ "saldo": 432}' echo -e "\e[93m\n---\e[0m" # Asking money credit to: rachelle echo -e "\e[93mAsking credit from \e[96mrachelle\e[0m" curl -i -H'Content-Type: application/json' -H'Authorization: Bearer FAKE.FAKE.FAKE' http://localhost:8080/credit -X PUT -d '{ "saldo": 485}' echo -e "\e[93m\n---\e[0m" # Asking money credit to: sandra echo -e "\e[93mAsking credit from \e[96msandra\e[0m" curl -i -H'Content-Type: application/json' -H'Authorization: Bearer FAKE.FAKE.FAKE' http://localhost:8080/credit -X PUT -d '{ "saldo": 500}' echo -e "\e[93m\n---\e[0m" # Asking money credit to: shikka echo -e "\e[93mAsking credit from \e[96mshikka\e[0m" curl -i -H'Content-Type: application/json' -H'Authorization: Bearer FAKE.FAKE.FAKE' http://localhost:8080/credit -X PUT -d '{ "saldo": 89}' echo -e "\e[93m\n---\e[0m"

askCredit.sh laburpena



Gure characters guztiak Nature Ligaren parte dira. Funtsean, banku-sistema honen parte izateko pertsona talde batzuk besterik ez. Testuinguru horretan, ingurumena defendatzen ari dira. Ez da oso garrantzitsua artikuluari dagokionez pertsona talde honek zer egiten duen edo non sartzen den istorioan, baina testuinguruari dagokionez, ingurumena defendatzeko eta klima-aldaketaren ondorioak moteltzeko ekintzetan parte hartzen dute. Gure characters batzuek denetarik egin dezakete, beste batzuek ezin dute ezer egin eta beste batzuek "kutxa" edo "kreditua eskatu" besterik ez dute egin. Kontuan izan, gainera, informazio sentikorra nahasten ari naizela. Token hauek normalean ez dira partekatu behar edo URL jakin batean ikusgai egon behar. Bai beti eskuragarri daude arakatzailearen garatzaileen kontsolaren bidez, baina, hala ere, egiten diren eskaera batzuk protect da. "Security-per-scurity" izenez ezagutzen den kontzeptua da and teknikoki erabiltzaileari erabiltzen ari den tokenaz jabetzea eragozten ez badu ere, disuasio gisa funtzionatzen du. Bi metodoetan, gordailua egiten dugunean edo kreditua eskatu, ohartu eskaera bakoitzeko, 1etik 500era arteko ausazko zenbaki bat bidaltzen ari garela. Orain ia prest gaude gure aplikazioa hasteko, baina lehenik eta behin, murgildu gaitezen pixka bat gehiago. teoria.

10. Nola egiten da JWT token bat




Orain gure tokenak sortu ditugula, ikus dezagun horietako bat. Lausotutako token bat erakutsiko dizut, eta hori ulertzeko erabiliko dugu.Hona hemen gure tokena: FAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKE . FAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETO . FAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKEN Hemen nabaritzea garrantzitsua dena da gure tokena hiru zatitan banatuta dagoela:

  • Goiburua - Hau Base64 kodetutako JSON konfigurazio goiburua da, goian aipatu dugun bezala.
  • Karga erabilgarria - Hau Base64 kodetutako JSON karga da. Hemen definitu genituen gure erreklamazio erreserbatuak eta pertsonalizatuak. Hemen ere defini ditzakegu erreklamazio pribatuak eta publikoak. Biak pertsonalizatutako erreklamazioen menpe daude. Ohar azkar gisa, nahi duguna egin dezakegu bi erreklamazio hauekin. Hala ere, erreklamazio publikoak IANA JSON Web Token Erregistroan definitutakoak dira. Garrantzitsua da gure tokenak modu jakin batean izendatzea, erregistroarekin talkak saihesteko. Erreklamazio publikoak hautazko estandar gisa ere defini daitezke. Erreklamazio pribatuek ez dute estandarrik jarraitzen eta guri dagokigu horiek definitzea.
  • Sinadura — Hemen sormen pixka bat izan gaitezke. Sinadura Header eta Payload konbinazio zifratua da. Erabili nahi dugun algoritmoa erabakitzen dugu eta tokenaren zati horrek, funtsean, erabakiko du bidaltzen ari garen mezua fidagarria den ala ez. Konbinazio hori bakarra da eta gure zerbitzariak sortu dugun "gako publikoa" erabiliko du parekorik ote dugun zehazteko. Goikotik gogoratzen baduzu RS256 erabiltzen ari gara gure adibidean.


Jarraitu aurretik, kontuan izan Header eta Payload decyphered daitezkeela gure adibidean. "Ezin" dugu karga edo goiburua manipulatu eta hala ere fidagarri bihurtzen. Token gaizto baten efektu potentzialen aurkako babesa guk aukeratzen dugun algoritmoaren bidez soilik babes daiteke. Beraz, aukeratu zuhurki. Ezkutuko informazioa kezkagarria den erakunde batean lan egiten baduzu, banku batean adibidez, mesedez EZ egin egingo duguna. Hau lokalean sortu ditugun token edukia sarean egiaztatzeko modu bat baino ez da. Lehenik eta behin, joan gaitezen https://jwt.io/ eta bete gure JWT tokena. Erabili sortu berri duzun tokena:


https://jwt.io/ erabiliz gure tokenaren edukia egiaztatzeko. Azter dezagun hemen duguna. Hau da gure administratzailearen tokena. Pertsona hori "Administratzailea" da gure adibidean. Ikus dezakegu gure parametro guztiak eskuragarri daudela. Gure zerrendan "sub", "aud", "upn", "access", "user_id", "iss", "name", "groups" eta azkenik "jti" ikusten dugu. Erreklamazio gehigarri batzuk ere baditugu. Ikus ditzagun:



" auth_time " — Hau da autentifikazioa gertatu da. Gure tokena 2022ko uztailaren 17an, igandea, 16:15:47 GMT+02:00 DST, autentifikatu denez" iat " — Hau da tokena sortu da. Gure kasuan, aldi berean gertatzen da auth_time." exp " — Hau da tokenaren iraungitze-data. 2022ko uztailaren 17an iraungiko da 16:32:27 GMT+02:00 DST. Ez dugu zehaztu iraungitze datarik gure tokenean. Horrek esan nahi du JWT ~15 minutuko balio lehenetsia erabiltzen duela.

Egin ditzagun orain proba batzuk.

11. Aplikazioa exekutatzea

Kodea prest dago GitHub- en erabiltzeko. Kodea egiaztatzen badugu eta Intellij-ekin irekitzen badugu, jakin behar dugu ezin dugula aplikazio hau Spring Boot aplikazio bat bezala exekutatu. Ez dago "psvm" exekutatu ahal izateko. Horren ordez, sortutako jar-a zuzenean exekutatu dezakegu eta ziurtatu aurretik "mvn build" bat egiten dugula. Hona hemen nola erabiltzen ari naizen momentu honetan:

[ ] https://github.com/jesperancinha/your-finance-je "Ingurumenaren konfigurazioa aplikazioa exekutatzeko")



Exekutatu dezagun orain " setupCertificates.sh " script-a berriro. Ez dakit zenbat denbora behar izan zenuten honaino iristeko baina oso litekeena da 15 minutuak momentu honetan jada pasata egotea. Badaezpada, exekutatu berriro. Abiarazi dezagun gure aplikazioa! Honela abi dezakegu:

 mvn clean install java -jar your-financeje-banking/target/your-financeje-banking.jar

Edo exekutatzeko prest dagoen gure konfiguraziotik exekutatu dezakegu. Begiratu repo eta Makefile aldez aurretik egiten duen guztia ulertu nahi baduzu:

 make dcup-full-action

Script honek 2 zerbitzu exekutatuko ditu. Bata 8080 portuan eta bestea 8081 portuan. 8080 atakan software honen bertsio bat exekutatuko dugu gure kodea exekutatuz JWT tokenak sortzeko. 8081 atakan, Adam Bien k sortutako jwtknizr sorgailua erabiliz bertsio bat exekutatuko dugu. Artikulu hau, hala ere, 8080 atakan exekutatzen den zerbitzuan zentratuko dugu. Nahi baduzu, cypress ere exekutatu dezakezu:

 make cypress-open

Horrek cypress kontsola open du, eta probak nahi duzun arakatzailearekin exekutatu ahal izango dituzu. Hala ere, arakatzailearen aukerak mugatuta daude oraindik fase honetan. Eskaera gehienak cypress ek emandako komando lerroko eskaerak izango dira. Oraingoz, ez gaitezen sartu " cypress "-era. Mesedez, joan zure arakatzailera eta joan kokapen honetara:

http://localhost:8080/accounts/all

Horrelako emaitza lortu beharko genuke:


Ikus dezakegunez, " Malory ", " Jack Fallout " eta " Jitska " ez dute inolako kreditu edo dirurik. Hau da, erabiltzaile-taldea bakarrik eman zaielako. Kontuan izan, gainera, Shikka ez zaiola krediturik eman. " Shikka ", taldeko krediturik ez duen gure bezero bakarra da. Erregistroak ikusten baditugu, eragiketa arrakastatsuek formatu hau hartzen dutela ikusiko dugu:

 Sending money to admin HTTP/1.1 200 OK Date: Sun, 17 Jul 2022 15:01:13 GMT X-Powered-By: KumuluzEE/4.1.0 Content-Type: application/json Content-Length: 32 Server: Jetty(10.0.9) {"balance":212,"client":"Admin"}


200 batek eragiketa arrakastatsua izan zela jakinarazten digu. "Malory", "Jack Fallout" eta "Jitska"-ren kasuan, bi eragiketak huts egiten dute eta orduan mezu hau jasoko dugu:

 Sending money to jitska HTTP/1.1 403 Forbidden X-Powered-By: KumuluzEE/4.1.0 Content-Length: 0 Server: Jetty(10.0.9)

403 batek jakinarazten digu gure JWT tokena balioztatu dela eta fidagarria dela. Hala ere, erabiltzaileari debekatuta dago eragiketa hori egitea. Beste era batera esanda, ez dute izendatutako metodorako sarbiderik.

Eskuratu ditzagun pixka bat gure tokenak. SendMoney.sh fitxategiaren token batzuk aldatzen baditugu. Hau lortu beharko genuke:

 Sending money to admin HTTP/1.1 401 Unauthorized X-Powered-By: KumuluzEE/4.1.0 WWW-Authenticate: Bearer realm="MP-JWT" Content-Length: 0 Server: Jetty(10.0.9)

Sartu pantaila osoko moduan Irten pantaila osoko moduan

401 honek esan nahi du gure tokena ez dela balioztatu. Esan nahi du zerbitzariak gure tokena fidagarria den egiaztatzeko erabiltzen duen gako publikoak ez duela bat-etorrik aurkitu. Gako publikoak ezin badu JWT tokenaren sinadura ebaluatu eta balioztatu, orduan baztertuko du.

Laburbilduz, goiburua eta "Payload" ez daude enkriptatuta. Oinarrizko 64 "kodetuta" besterik ez dira. Horrek esan nahi du "Deskodetzeak" karga erabilgarria zer den beti ikusteko aukera ematen duela. Gure karga entzuteetatik babestu nahi badugu, tokenaren "Payload" ez dugu erabili behar identifikazio-parametroak hautatzeko beste ezertarako. Arazoa benetan norbaitek JWT tokena eskuratzen duenean, adibidez, TLS tunela arriskuan jarri denean eta norbaitek trukatutako mezuen edukia irakurtzeko gai denean. Hori gertatzen denean, beste babes bat dago oraindik. Eta hau da sinadura. Sartzen den mezu bat balioztatzeko gai den bakarra gako publikoa duen zerbitzaria da. Gako publiko honek, publikoa izan arren, sarrerako mezua balioztatzeko aukera ematen du soilik sinaduraren eta "Goiburua + karga"ren aurka exekutatzen.

12. Ondorioa

Gure saioaren amaierara iritsi gara. Eskerrik asko hau jarraitzeagatik. Ikus dezakegu JWT tokenak nola trinkoak diren eta XML-ren parekoak, SAML tokenak, baino askoz ere zehatzagoak diren. Ikusi dugu zein erraza den tokenak sortzea eta erabiltzea metodo jakin batzuetarako beharrezkoak diren baimen batzuk lortzeko eta nola iristen garen sinatutako token baten bidez. Hala ere, oso garrantzitsua iruditzen zait JWT nola funtzionatzen duen jakiteko. Zorionez, honekin JWT tokenek nola funtzionatzen duten ezagutzeko sarrera ona eman dizut. Horrek guztiak nola funtzionatzen duen hobeto ezagutzeko, inplementatutako cypress probekin jolastea gomendatzen dizut. Eskaerak nola egiten diren eta zer probatzen ari garen eta zer espero den ikusteko modu bikaina da. Orduan, erabiltzaile batzuek eragiketa batzuk eta beste batzuk ez zergatik egiten dituzten jakiteko hobeto ulertuko duzu. Aplikazio honen iturburu-kode guztia GitHub- en jarri dut. Espero dut artikulu hau nik idazten nuen bezainbeste gustatu izana. it.Eskerrik asko irakurtzeagatik!

13. Erreferentziak