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.
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.
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:
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.
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.
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.
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 dezakeguuser_id
" — Hau erabiliko dugu erabiltzailearen IDa ezartzeko.access
" — Erabiltzailearen sarbide-maila zehaztuko dugu.name
" — Erabiltzailearen izena. 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.
Ikus dezagun erabiltzen ari diren zerbitzuen zerrenda:
Mota,URLa,Payload,Emaitza,Baimendutako rolak
POST,
POST,
LORTU,
JARRI,
LORTU,
LORTU,
LORTU,
JARRI,
LORTU,
LORTU,
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:
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.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.
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:
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.
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.
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!