paint-brush
Waarom JWT die sleutel is om u ondernemingsprogram te beveilig - en waarom KumuluzEE u nuwe beste vriend kan weesdeur@jesperancinha
Nuwe geskiedenis

Waarom JWT die sleutel is om u ondernemingsprogram te beveilig - en waarom KumuluzEE u nuwe beste vriend kan wees

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

Te lank; Om te lees

'JWT' of JavaScript Object Notation Web Token is 'n standaard gedefinieer in [ RFC7519] Dit kan op verskeie maniere gedefinieer word en kan gebruik word om inligting tussen twee partye oor te dra. In hierdie artikel sal ons kyk hoe om 'JWT' in 'n algemene Java-ondernemingstoepassing te integreer.
featured image - Waarom JWT die sleutel is om u ondernemingsprogram te beveilig - en waarom KumuluzEE u nuwe beste vriend kan wees
João Esperancinha HackerNoon profile picture
0-item

Deesdae het ons meer en meer kommer oor prestasie, en terselfdertyd wil ons weet hoe stelsels vinnig en betroubaar kan kommunikeer. Ons wil baie keer inligting stuur en dit sover moontlik vertroulik en veilig hou. Sensitiewe data moet soms ook publiek deur die web beweeg en aksies aan die ander kant van die draad veroorsaak. Meestal wil ons aksies genereer wat datamutasies sal veroorsaak. In hierdie gevalle kyk ons nie net na die beskerming van ons data nie. Ons wil seker maak dat die aksies wat veroorsaak word deur die stuur van ons data, vertrou word. Ons kan ons data op verskeie maniere beskerm. Meestal stuur ons die data via 'n TLS (Transport Layer Security) veilige verbinding. Dit sal verseker dat ons data deur die draad geïnkripteer word. Ons gebruik sertifikate om vertrouensverhoudings tussen twee partye te skep en dit te bereik. In hierdie artikel wil ek die JWT standaard bespreek en verder sien hoe ons JWT in 'n algemene Enterprise toepassing kan integreer. In hierdie geval sal ons na KumuluzEE kyk. Kom ons kyk na 'n paar basiese konsepte. JWT of JSON Web Token, of nog beter, JavaScript Object Notation Web Token, is 'n standaard wat in RFC7519 gedefinieer word. Hierdie standaard is, soos alle RFC (Request For Comments) standaarde, gedefinieer, geskryf en gepubliseer deur die IETF (Internet Engineering Task Force). Dit kan op verskeie maniere gedefinieer word. Oor die algemeen kan ons sê dat JWT 'n kompakte, veilige vorm is om eise tussen twee partye oor te dra. Een manier om te vereenvoudig wat 'n eis is, is basies om dit te beskryf as 'n naam/waarde-paar wat inligting bevat. Ons het hierdie inligting nodig om 'n paar belangrike aspekte van ons internetkommunikasie te waarborg. Ons moet seker maak dat die inligting wat ons ontvang in die eerste instansie bekragtig en vertrou word. Dan moet ons dit bekragtig. Dit is basies dit.Om hierdie standaard te implementeer, kan ons verskeie raamwerke gebruik wat ons kan help om 'n Java-ondernemingstoepassing te implementeer. Spring Boot word wyd gebruik. Baie keer word dit ook onder 'n ander naam toegedraai in behoorlike sagteware van sekere organisasies soos banke en ander finansiële organisasies. Vir ons voorbeeld het ek besluit om iets anders te doen. In plaas van Spring Boot, gaan ons na 'n voorbeeld met KumuluzEE kyk. Die punt is om presies te identifiseer wat JWT is en hoe dit lyk. Java Enterprise Applications is basies toepassings wat in 'n toepassingsbediener ontplooi kan word of net op hul eie kan hardloop deur die gebruik van 'n ingebedde bediener. As 'n voorbeeld, Spring Boot-toepassings loop op 'n ingebedde Tomcat-bediener. In hierdie artikel sal ons fokus op KumuluzEE gestel word. Net soos Spring Boot bevat dit ook 'n ingebedde bediener. Behalwe dat dit in hierdie geval Jetty genoem word. Dit word in kombinasie met Weld gebruik om CDI (Context Dependency Injection) te verskaf. Alle Java EE en Jakarta EE tegnologie standaarde is versoenbaar met hierdie framework .

2. Geval Voorbeeld


Om 'n voorbeeld te gee van hoe JWT in sy basiese vorm werk, moes ek aan 'n manier dink om dit aan te bied. Klassieke voorbeelde waar sekuriteit 'n bekommernis is, is banke. Om egter 'n hele bankaansoek te maak om te wys hoe JWT werk, sal 'n mors van tyd wees en miskien sal te veel konsepte betrokke wees. In plaas daarvan, wat ek gemaak het, is 'n baie eenvoudige bankstelsel. Ons grootste bekommernis is om te wys hoe data deur die draad vloei en hoe gebruikers toegang tot sekere areas van ons toepassing kry. Ek gaan ook nie TLS bespreek of hoe ons geïnkripteer inligting deur die draad kan stuur nie. Ons sal ons fokus op JWT in sy suiwerste vorm behou. Ons saak is 'n bankstelsel wat gebruik word deur 'n groep wat die natuur en die omgewing verdedig. Dit is net 'n prettige manier om te wys hoe JWT werk. Die hoofkarakter van hierdie Liga van die Natuur is Lucy, wat 'n algemene karakter in al my artikels word.

3. Argitektuur

Voordat ons begin, kom ons skets net ons lopende toepassing. Dit is 'n baie eenvoudige toepassing, maar dit is steeds 'n goeie ding om dit te teken:

Die rede waarom dit so eenvoudig is, is dat aangesien JWT op elke versoek nagegaan word en elke versoek teen die publieke sleutel geverifieer word, ons weet dat solank ons die korrekte teken op elke versoek stuur, ons sal kan deurkom. JWT kan geïntegreer word met OAuth2, Okta SSO of enige ander magtigingsmeganisme. In hierdie geval is wat ons doen om verifikasie en magtiging te vestig. In ons toepassing gaan ons JWT gebruik en daarmee ons boodskap verifieer met 'n handtekening. Ons sal egter nie by die toepassing aanmeld nie. In plaas daarvan sal ons gebruikers magtig om ons toepassing te gebruik na suksesvolle verifikasie. Op hierdie stadium is dit maklik om te sien dat JWT in sy kern eintlik 'n baie klein deel van 'n volledige toepassing is. Nietemin moet sekere funksies bygevoeg word. Dit is die hulpbronne wat ons benodig:

  • Balansisteem
  • Kredietstelsel


Kom ons sê net dat ons basiese stelsel net geld- en kredietversoeke sal registreer. In wese sal dit net waardes ophoop. Kom ons neem ook aan dat sommige mense krediet sal kan kry en ander nie. Sommige mense sal geld kan stoor en ander mense sal krediet kan kry.

4. Die keuse van tegnologieë

Soos in die inleiding genoem, sal ons KumuluzEE as ons ondernemingstoepassingsraamwerk gebruik, en ons sal 'n ultra-basiese toepassing implementeer op 'n manier dat ons na basiese JWT terminologie en -konsepte kan kyk. Maak seker dat u die korrekte Java-weergawe het. Op hierdie stadium sal ons 'n minimum Java 17 SDK moet installeer. Ons benodig maven, git, 'n Java-versoenbare IDE soos IntelliJ, en 'n soort dop.

5. Opstelling

Om ons toepassing te begin, het ons 'n paar KumuluzEE afhanklikhede. Dit is hoofsaaklik omdat KumuluzEE , net soos Spring Boot, 'n paar afhanklikhede benodig. Kom ons kyk kortliks na die POM-lêer:

 <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>

Kom ons bespreek 'n paar afhanklikhede kortliks. Soos jy dit lees, volg asseblief ons pom.xml -lêer van bo na onder. Dit is belangrik om die volgende verduideliking te verstaan. Ons benodig 'n pakket van afhanklikhede om ons toepassing te laat werk. , Gelukkig bied KumuluzEE aan ons mikroprofielbiblioteke wat basiese standaardbundels bevat om hierdie toepassing te begin. Dit is alles vervat in die KumuluzEE -Mikroprofiel-biblioteek. Om ons toepassing te kan opstel met al die JWT -parameters wat ons benodig, moet ons 'n MicroProfile-biblioteek daarby voeg. Terselfdertyd benodig ons 'n JSON-verwerkingsbiblioteek. Dit sal wees wat Johnson Core doen. Ons het natuurlik die kern van KumuluzEE nodig om te werk. Jetty is die onderliggende bediener wat die KumuluzEE raamwerk bestuur. Dit is hoekom ons dit nodig het in ons afhanklikhede. Aangesien ons CDI benodig, benodig ons ook 'n biblioteek wat dit ondersteun. Om ons REST-eindpunte te aktiveer, het ons die resbiblioteek van KumuluzEE nodig. Om ons API te kry, benodig ons dan 'n Geronimo-biblioteek. Dit sal verseker dat ons 'n implementering van JSR-374 beskikbaar het. Ons moet ook ons JWT en sy JSON-formatted inhoud interpreteer. Lombok is nie regtig per se nodig nie. Dit maak alles net mooi en blink! Logback is ook belangrik om te hê sodat ons logboeke beter kan interpreteer en ons resultate kan verstaan. Kom ons kyk nou na ons resources lêergids. Om te begin, kom ons verstaan eers wat ons verwag om in hierdie gids te vind. Ons moet ons toepassing opstel met iets wat verband hou met JWT , Logback en laastens moet ons iets sê oor die bone wat ons gaan skep. Kom ons kyk na die eenvoudigste lêer daar. Die boontjies.xml kan gevind word in META-INF:

 <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>

Dit is net 'n tipiese en soos jy dalk nou dink, 'n bietjie van 'n ou lêer. Op hierdie stadium is die idee net om KumuluzEE aan die gang te kry. Ons het wel 'n uitsluit-aksie. Dit sê vir Weld om nie klasrekeninge in sy skandering vir boontjies-aksie in ag te neem nie. Dit is belangrik, want met die implementering wat ons gebruik, sal Weld basies elke klas met 'n leë konstruktor as 'n boontjie beskou. Ons sal later sien hoekom ons nie wil hê dat rekeninge as 'n boontjie beskou moet word nie. Laat ons vir die oomblik in gedagte hou dat ons versoeke onder die Versoek-omvang rig. Dit is logies omdat elke versoek 'n ander gebruiker kan hê. Kom ons kyk nou hoe " logback " geïmplementeer word. Dit word ook gevind in META-INF :

 <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>

Dit is net 'n baie eenvoudige konfigurasie vir ons logs . Ten slotte, miskien die belangrikste lêer van ons toepassing. Dit is die config-sjabloon. Op hierdie stadium is dit belangrik om daarop te let dat sommige van die lêers wat ek in hierdie projek geskep het, deel is van 'n sjabloonstruktuur. Ek sal later meer daaroor verduidelik. Hierdie sjabloonlêer is veronderstel om in 'n config.yml-lêer verander te word wat deur MicroProfile gelees sal word. Hierdie lêer is by die wortel van hulpbronne geleë:

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

Ons sal later sien wat presies al hierdie eiendomme eintlik beteken. Almal van hulle is selfverduidelikend. Die publicKey en uitreiker is almal parameters wat vervang sal word. Ons sal dit later ondersoek. Ons bash-skrifte sal seker maak dat hulle vervang word. Ons is amper gereed om te gaan kodeer, maar kom ons kyk eers na ons JWT tokenstruktuur.

6. Hands-on kode

Kom ons maak ons baie klein aansoek. Hierdie afdeling sal verduidelik hoe ons ons aansoek kan kry om met JWT te werk. Wat ons wil sien, is of ons gebruikers kan spesifiseer om toegang te verkry tot sommige van ons REST metodes en nie ander nie. Een van die maniere om na hierdie kode te begin kyk, is om eers na ons gewone JWT token te kyk. Hier is ons admin voorbeeld:

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

Daar word na elkeen van hierdie name in ons JSON verwys as eise. In ons voorbeeld sien ons 'n paar Voorbehou-aansprake:

  • " iss " - Dit is die uitreiker van die teken. Ons kan arbitrêr 'n waarde hiervoor kies. Die waarde van hierdie parameter moet ooreenstem met die uitreiker veranderlike wat vervang moet word in die config.yml wat ons voorheen gesien het.
  • " jti " - Dit is 'n unieke identifiseerder van die teken. Ons kan byvoorbeeld hierdie eis gebruik om te verhoed dat 'n teken twee of meer keer gebruik word.
  • " sub " - Dit is die onderwerp van die teken. Dit kan die gebruiker wees of enigiets waarvan ons hou. Dit is belangrik om in gedagte te hou dat dit ook gebruik kan word as 'n identifiseerder, sleutel, benaming of enigiets wat ons wil hê.
  • " upn " — Gebruikershoofnaam. Dit word gebruik om die skoolhoof te identifiseer wat die gebruiker gebruik.
  • " groups " — Dit is 'n reeks van die groepe waaraan die huidige gebruiker behoort. Dit sal in wese bepaal wat 'n versoek met hierdie teken kan doen. In ons teken sien ons dan 'n paar persoonlike eise. Ons kan dit net so goed gebruik as die Reserve-eise
  • " user_id " — Ons sal dit gebruik om die gebruiker-ID te stel.
  • " access " — Ons sal die toegangsvlak van die gebruiker bepaal.
  • " name " — Die naam van die gebruiker.

7. Hands-on kode

Kom ons maak 'n opsomming van wat ons tot dusver weet. Ons weet ons sal met tekens kommunikeer met 'n struktuur wat ons bepaal het. Verder het ons die konfigurasie van ons toepassing, die logback-konfigurasie opgestel en laastens het ons 'n pasgemaakte konfigurasie vir die ondernemingsboonopsoek opgestel. Kom ons kyk na die pakketmodel. Hier sal ons 3 klasse vind. Hierdie klasse verteenwoordig basies net 'n samevoeging van rekeninge en die verteenwoordiging tussen client en account . Op hierdie manier kan ons begin deur na kotlin-lêer Model.kt te kyk waar Client geleë is by:

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

Hierdie eerste modelklas is die voorstelling van ons kliënt. Ons client vir ons saak het net 'n naam. Dit is die gebruikersnaam wat deur die " jwt " kenmerknaam verteenwoordig word. Verder het ons Account :

 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)) ) }


In hierdie klas stel ons basies 'n rekeningnommer, 'n kliënt, 'n huidige waarde en laastens 'n kredietwaarde op. Let daarop dat ons alle waardes na 0 stel. Ons gebruik ook BigDecimal, bloot omdat ons met geld te doen het. Geld moet presies wees en kan nie stelsel-afrondings of -afrondings ondervind nie. Dit beteken met ander woorde en as 'n voorbeeld dat 'n getal soos 0. 0000000000000000000000000000000000000000000000000001 euro die hele tyd daardie getal moet bly. Ons wil ook waardes by ons rekening voeg. Dit is waar die metode addCurrentValue ontstaan. Om dieselfde redes sal ons ook ons krediet aanvul met die addCreditValue . Ten slotte, in die laaste stuk van ons data-opstelling kom ons Accounts teë:

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

Dit is in wese net 'n versamelaar van al ons rekeninge. Ons sal sy kaartinhoud gebruik om die gedrag van 'n databasis na te boots. Kom ons kyk nou na die kontroleerderpakket. Dit is waar ons ons toepassing skep wat met ons datamodel loop. Kom ons kyk eers na klas BankApplication :

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


Hiermee sê ons 3 belangrike dinge. Met die LoginConfig-aantekening definieer ons dit om JWT -tokens volgens MicroProfile te gebruik en te verstaan. Die ApplicationPath definieer die toepassingswortel. Dit is waar die URL van die toepassing sal begin. In ons voorbeeld sal dit HTTP://localhost:8080 wees. Laastens definieer die DeclareRoles die rolle wat deur ons aansoek gebruik en aanvaar gaan word. Rolle en Groepe is uitruilbare terme in hierdie situasie. Om inspuiting doeltreffend te laat werk, skep ons 'n aantekening spesifiek om die rekeningkaart te identifiseer:

 annotation class AccountsProduct

Gaan in volskermmodus Verlaat volskermmodus

Vervolgens skep ons 'n kasvoorwerpfabriek AccountsFactory:

 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() } } }


Hierdie fabriek is die rede waarom ons opsoek spesifiek vir Accounts gedeaktiveer het. In plaas daarvan om die opsoekproses toe te laat om 'n boontjie te skep, skep ons self die aggregator-instansie. Deur die Produseer-aantekening te gebruik, kan ons die boontjie skep. Met behulp van ons pasgemaakte aantekening, AccountsProduct, maak ons die gebruik van hierdie boontjie meer spesifiek. Ten slotte, deur ApplicationScoped te gebruik, definieer ons die omvang daarvan as die Application . Met ander woorde, die rekeningaggregasieboon sal optree as 'n enkelton-objek regoor die toepassing. Die " createResponse " is net 'n generiese metode om JSON-antwoorde te skep. Wat ons nou nodig het, is twee "Hulpbronne". Dit is basies dieselfde as " Controllers " in die lente. Dit is 'n ander naam, maar dit het presies dieselfde gebruik. Kom ons kyk na die AccountsResource -klas:

 @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) } }

Neem 'n oomblik om meer in detail na hierdie klas te kyk. Die Path -aantekening definieer hoe om hierdie hulpbron vanaf die wortel te bereik. Onthou dat ons "/" as die wortel gebruik. In hierdie geval is "rekeninge" ons worteltoegangspunt vir hierdie hulpbron. Al ons bronne, in ons geval is net twee wat met omvang RequestResource loop. Met annotasie bepaal Produces dat alle antwoorde op alle versoeke, ongeag hul tipe, die vorm van JSON-geformateerde boodskappe sal aanneem. Om ons aggregator in te spuit, gebruik ons net die kombinasie van die Inject annotation en AccountsProduct annotasie:

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


Dit pas by wat ons in die fabriek gedefinieer het. Verder spuit ons ook twee belangrike elemente van sekuriteit in. 'n principal en die jsonWebToken :

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


Beide JsonWebToken en Principal sal dieselfde wees, en ons sal dit in ons logboeke sien. In ons hulpbronne kan ons altyd eise van 'n versoek met 'n sekere teken inspuit:

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


Dit word bereik met die kombinasie van die Inject en Claim -aantekeninge. Die naam wat onder die Claim -aantekening geplaas word, definieer watter eis ons wil invoeg. Ons moet versigtig wees met die tipe waarmee ons ons parameters definieer. In ons voorbeeld het ons net JsonString en JsonNumber -tipes nodig. Kom ons kyk eers hoe ons rekeninge en gebruikers skep:

 @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() ) ) }

Die skep van rekeninge en gebruikers


Die doel hier is om metodes te kan skei en hulle verskillende toestemmings te gee. In ons voorbeeld skep hulle albei net 'n rekening, maar dit is belangrik om op te let dat slegs gebruikers met rollegebruikers die createUser-metode kan gebruik. Op dieselfde manier kan slegs gebruikers met rolle van kliënt en krediet toegang kry tot die metode createAccount. Kom ons kyk nou in detail na die PUT-versoekmetode van hierdie hulpbron:

 @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) }

Inbetaal


Ons weet dat aantekening PUT aandui dat hierdie metode slegs toeganklik is met versoeke van tipe PUT . Annotation Path sê dan vir Jetty dat die pad na hierdie metode 'n waarde is. Dit staan ook bekend as 'n PathParam . Laastens kan ons hierdie metode definieer wat slegs toegelaat word om gebruik te word deur gebruikers met rolle admin of kliënt. Die invoerwaarde word dan deur die gebruik van die PathParam na ons Langwaarde-veranderlike oorgedra. As ons geen rolle definieer nie, sal enige gebruiker met die regte token toegang tot hierdie metodes kan verkry. CreditResource word in dieselfde geïmplementeer manier:

 @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) } }

Die enigste verskil is dat in plaas daarvan om rolle admin en client te gebruik, gebruik ons nou admin en credit rolle. Let ook op dat rekeninge vir gebruikers nooit in hierdie resource geskep sal word nie. Dit is slegs moontlik via die rekening se resource . Noudat ons weet hoe die kode geïmplementeer word, laat ons eers opsom watter metodes ons in ons REST diens beskikbaar gestel het.

8. Toepassingsgebruik

Kom ons kyk na die lys van die dienste wat gebruik word:


Tik, URL, Loonvrag, Resultaat, Rolle toegelaat
PLAAS, http://localhost:8080/accounts,n/a,geskep rekening, admin/kliënt/krediet
PLAAS,
http://localhost:8080/accounts/user,n/a,geskep gebruiker, admin/gebruiker
KRY,
http://localhost:8080/accounts,n/a,Matching rekening, admin/kliënt
PUT,
http://localhost:8080/accounts,{saldo: Long}, Huidige balans, admin/kliënt
KRY,
http://localhost:8080/accounts/all,n/a,All lopende rekeninge, Alle
KRY,
http://localhost:8080/accounts/summary,n/a,Som van alle saldo's, Alle
KRY,
http://localhost:8080/credit,n/a,Matching rekening, admin/kliënt
PUT,
http://localhost:8080/credit,{saldo: Long}, huidige krediet, admin/kliënt
KRY,
http://localhost:8080/credit/all,n/a,All krediete, Alle
KRY,
http://localhost:8080/credit/summary,n/a,Som
krediete, Alle

9. Genereer toetsomgewing

Ek het 'n bash -lêer in die wortelgids geskep. Hierdie lêer word "setupCertificates.sh" genoem. Kom ons kyk daarna om 'n idee te kry van wat dit doen:

 #!/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

Omgewing generasie

Volg asseblief die lêer terwyl ek verduidelik wat dit doen. Dit is belangrik sodat ons presies verstaan wat dit doen. Ons skep eers private en publieke sleutels in 'n PEM formaat. Ons gebruik dan die private sleutel met ons hardloopbare "your-finance-jwt-generator.jar" . Dit is ons loopbare pot wat die vinnige skepping van tokens moontlik maak. Die uitreiker kan nie later verander word nie. Uiteindelik skep dit 'n teken. Ons sal later sien hoe om hierdie teken te lees. Hierdie teken bevat 3 ekstra Kop-eise. Dit is "kind", "typ" en "alg". Dit volg die volgende formaat:

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

Die kop van die JWT

Kom ons kyk van naderby na hierdie bewerings:

  • "kind" - Werk as 'n wenk-eis. Dit dui aan watter soort algoritme ons gebruik.
  • "typ" - Dit word gebruik om IANA mediatipes te verklaar. Daar is drie opsies JWT (JSON Web-token), JWE (JSON Web Encryption), en JWA (JSON Web Algorithms). Hierdie tipes is nie relevant vir ons eksperiment nie. Ons sal net sien dat ons teken nie regtig goed geïnkripteer is nie en dat dit regtig maklik is om dit te dekripteer. Ons sal ook sien dat alhoewel ons tokens kan dekripteer, ons nie so maklik kan peuter om ander aksies uit te voer nie.
  • "alg" - Dit is hoe ons die handtekeningtipe definieer wat ons wil gebruik. Ondertekening kan beskou word as 'n kriptografiese operasie wat sal verseker dat die oorspronklike teken nie verander is nie en vertrou word. In ons geval gebruik ons RS256 andersins bekend as RSA Signature met SHA-256.

Met ons publieke sleutel kan ons dit uiteindelik gebruik om ons sjabloon te verander. Die nuwe config.yml-lêer moet so lyk:

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

config.yml


Die tweede stap is om vier lêers te skep. Vir elke enkele gewone token in die gids " jwt-plain-tokens ", sal ons vier opdragte skep. Die eerste opdrag is om gebruikers te skep wat effektief dinge met hul rekeninge kan doen. Dit is gebruikers met profiele " admin ", " client " en " credit ". Kom ons hardloop die lêer " createAccount.sh ", om hulle te skep. Die tweede opdrag sal die res van die gebruikers skep wat nog geen regte het nie. Dit is die lêer "createUser.sh". Kom ons bestuur dit. Nou sal ons sien dat alle gebruikers uiteindelik geskep is. Kom ons kyk nou na besonderhede oor transaksies en kyk na die oorblywende twee opdragte. Een om te "cashin" en 'n ander om meer krediet te vra. Die eerste gegenereerde lêer is die "sendMoney.sh" bash script. Hier kan ons alle versoeke vind om te " cashin ". In hierdie lêer sal jy 'n krulversoek vind om ewekansige geldhoeveelhede per gebruiker aan gebruikers te stuur. Kom ons kyk na die admin saak:

 #!/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 uittreksel

Dieselfde gebruikers het ook hul kredietversoeke aan hulle toegewys:

 #!/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 uittreksel



Al ons characters is deel van die Liga van Nature . In wese net 'n groep mense om deel te wees van hierdie bankstelsel. In hierdie konteks verdedig hulle die omgewing. Dit is nie regtig relevant vir die artikel wat hierdie groep mense doen of waar in die storie hulle inpas nie, maar vir konteks neem hulle deel aan aksies om die omgewing te verdedig en die uitwerking van klimaatsverandering te vertraag. Sommige van ons characters kan alles doen, ander kan niks doen nie en ander kan net “cashin” of net “vra vir krediet”. Let ook op dat ek sensitiewe inligting vertroebel. Hierdie tekens moet normaalweg nie gedeel word of sigbaar wees op 'n spesifieke URL nie. Hulle is ja, altyd beskikbaar via die blaaier-ontwikkelaarkonsole, maar dit is in elk geval om sommige versoeke wat gemaak word, protect . Dit is 'n konsep wat bekend staan as "sekuriteit-per-duisternis" and hoewel dit nie tegnies verhoed dat die gebruiker bewus word van die teken wat gebruik word nie, werk dit wel as 'n afskrikmiddel. In beide metodes, wanneer ons 'n deposito maak of wanneer ons vra vir krediet, let op dat ons vir elke versoek 'n ewekansige getal tussen 1 en 500 stuur. Ons is nou amper gereed om ons aansoek te begin, maar eers, laat ons 'n duik in 'n bietjie meer teorie neem.

10. Hoe word 'n JWT token gemaak




Noudat ons ons tokens gegenereer het, kom ons kyk na een van hulle. Ek gaan vir jou 'n verduisterde teken wys, en ons gaan dit gebruik om dit te verstaan. Hier is ons teken: FAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKE . FAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETO . FAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKEN Wat hier belangrik is om op te let, is dat ons teken in drie dele verdeel is:

  • Kop - Dit is 'n Base64-gekodeerde JSON-konfigurasie-kop, net soos ons hierbo bespreek het.
  • Loonvrag - Dit is 'n Base64-gekodeerde JSON-loonvrag. Dit is waar ons ons Gereserveerde en Pasgemaakte eise gedefinieer het. Ons kan hier ook Private en Openbare eise definieer. Beide van hulle val onder Custom eise. As 'n vinnige nota, kan ons doen wat ons wil met albei hierdie eise. Daar word egter na openbare eise verwys as dié wat in die IANA JSON Web Token Registry gedefinieer word. Dit is belangrik dat ons ons tokens op 'n sekere manier benoem om botsings met die register te vermy. Openbare eise kan ook as standaard opsioneel gedefinieer word. Private eise volg geen standaard nie en dit is aan ons om dit te definieer.
  • Handtekening - Dit is waar ons 'n bietjie kreatief kan wees. Die handtekening is 'n gesyferde kombinasie van die Header en die Payload . Ons besluit die algoritme wat ons wil gebruik en hierdie deel van die teken sal basies bepaal of die boodskap wat ons stuur, vertrou moet word. Dit is uniek aan daardie kombinasie en ons bediener sal die "publieke sleutel" wat ons geskep het gebruik om te bepaal of ons 'n pasmaat het. As jy van bogenoemde onthou gebruik ons RS256 in ons voorbeeld.


Voordat ons voortgaan, let asseblief daarop dat beide die Header en die Payload in ons voorbeeld decyphered kan word. Ons "kan" net nie met die loonvrag of die kopstuk peuter en dit steeds vertrou nie. Die beskerming teen die potensiële gevolge van 'n kwaadwillige teken kan slegs beskerm word deur die algoritme wat ons kies. Kies dus verstandig. As jy in 'n organisasie werk waar hoogs geheime inligting 'n bekommernis is, soos 'n bank, MOET asseblief NIE doen wat ons gaan doen nie. Dit is slegs 'n manier vir ons om die inhoud van die tokens wat ons plaaslik gegenereer het aanlyn na te gaan. Kom ons gaan eers na https://jwt.io/ en vul ons JWT token in. Gebruik die teken wat jy pas gegenereer het:


Gebruik https://jwt.io/ om die inhoud van ons teken na te gaan. Kom ons ondersoek wat ons hier het. Dit is ons administrateur-token. Daardie persoon is "Admin" in ons voorbeeld. Ons kan sien dat ons parameters almal beskikbaar is. In ons lys sien ons "sub", "aud", "upn", "access", "user_id", "iss", "name", "groups" en laastens die "jti". Ons het ook 'n paar ekstra eise. Kom ons kyk na hulle:



" auth_time " — Dit is wanneer die verifikasie plaasgevind het. Ons teken soos geverifieer op Sondag, 17 Julie 2022 16:15:47 GMT+02:00 DST" iat " — Dit is wanneer die teken geskep is. In ons geval gebeur dit gelyktydig as die auth_time." exp " - Dit is die vervaldatum van die teken. Dit verval op Sondag 17 Julie 2022 16:32:27 GMT+02:00 DST. Ons het geen vervaldatum in ons teken gespesifiseer nie. Dit beteken dat JWT sy verstekwaarde van ~15 minute gebruik.

Kom ons doen nou 'n paar toetse.

11. Begin die toepassing

Die kode is gereed om op GitHub gebruik te word. As ons die kode nagaan en dit met Intellij oopmaak, moet ons bewus wees dat ons nie hierdie toepassing soos 'n Spring Boot-toepassing kan laat loop nie. Daar is geen "psvm" om dit te laat loop nie. In plaas daarvan kan ons net die gegenereerde jar direk laat loop en seker maak dat ons net voor 'n "mvn build" maak. Hier is hoe ek dit op die oomblik gebruik:

[ ] https://github.com/jesperancinha/your-finance-je "Omgewingsopstelling om die toepassing te laat loop")



Laat ons nou weer die " setupCertificates.sh "-skrip laat loop. Ek weet nie hoeveel tyd jy geneem het om hier te kom nie, maar dit is baie waarskynlik dat die 15 minute reeds verby is op hierdie stadium. Net vir ingeval, laat hulle net weer hardloop. Kom ons begin ons toepassing! Ons kan dit so begin:

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

Of ons kan dit net deur ons gereed-om-te-loop-konfigurasie laat loop. Gaan die repo en die Makefile vooraf na as jy alles wil verstaan wat dit doen:

 make dcup-full-action

Hierdie skrip sal 2 dienste laat loop. Een op poort 8080 en die ander op poort 8081 . Op poort 8080 sal ons 'n weergawe van hierdie sagteware laat loop met ons eie kode om JWT tokens te genereer. Op poort 8081 sal ons 'n weergawe gebruik met behulp van die jwtknizr kragopwekker geskep deur Adam Bien . Ons sal hierdie artikel egter fokus op die diens wat op poort 8080 loop. As jy wil, kan jy ook cypress hardloop met:

 make cypress-open

Dit sal die cypress open , en jy sal die toetse met die blaaier van jou keuse kan uitvoer. Blaaieropsies is egter steeds beperk op hierdie stadium. Die meeste van die versoeke sal eintlik opdragreëlversoeke wees wat deur cypress verskaf word. Kom ons gaan vir eers nie na " cypress " nie. Gaan asseblief na jou blaaier en gaan na hierdie ligging:

http://localhost:8080/accounts/all

Ons behoort 'n resultaat soos hierdie te kry:


Soos ons kan sien, het " Malory ", " Jack Fallout " en " Jitska " geen krediet of geld nie. Dit is omdat hulle slegs die gebruikersgroep gegee is. Let ook op dat Shikka geen krediet gekry het nie. " Shikka ", is ons enigste kliënt wat nie die groepkrediet het nie. As ons na die logboeke kyk, kan ons sien dat suksesvolle bedrywighede hierdie formaat aanneem:

 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"}


'n 200 laat ons weet dat die operasie suksesvol afgeloop het. In die geval van "Malory", "Jack Fallout" en "Jitska", misluk albei operasies en dan sal ons hierdie soort boodskap kry:

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

'n 403 laat ons weet dat ons JWT token bekragtig is en dat dit vertrou word. Die gebruiker word egter verbied om daardie operasie uit te voer. Met ander woorde, hulle het geen toegang tot die aangewese metode nie.

Kom ons peuter bietjie met ons tokens. As ons sommige van die tekens van die sendMoney.sh-lêer verander. Ons behoort hierdie te kry:

 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)

Gaan in volskermmodus Verlaat volskermmodus

Hierdie 401 beteken dat ons teken nie bekragtig is nie. Dit beteken dat die publieke sleutel wat die bediener gebruik om te kyk of ons token vertrou moet word, geen ooreenstemming gevind het nie. As die publieke sleutel nie die handtekening van die JWT-token kan evalueer en bekragtig nie, sal dit dit dan verwerp.

As 'n opsomming, die kopskrif en die "loonvrag" is nie geïnkripteer nie. Hulle is net basis 64 "geënkodeer". Dit beteken dat "Dekodering" ons altyd in staat stel om binne te kyk wat die loonvrag eintlik is. As ons soek om ons loonvrag te beskerm teen afluistering, moet ons nie die "Payload" van die teken vir enigiets anders gebruik as om identifikasieparameters te kies nie. Die probleem lê eintlik wanneer iemand die JWT -token in die hande kry, byvoorbeeld wanneer die TLS-tonnel gekompromitteer is en iemand die inhoud van die uitgeruilde boodskappe kan lees. Wanneer dit gebeur, is daar nog 'n ander beskerming. En dit is die handtekening. Die enigste een wat 'n boodskap wat ingaan kan valideer, is die bediener wat die publieke sleutel bevat. Hierdie publieke sleutel, hoewel publiek, laat slegs toe om die inkomende boodskap te bekragtig deur teen die handtekening en die "Header + Payload" te hardloop.

12. Gevolgtrekking

Ons het die einde van ons sessie bereik. Dankie dat jy dit volg. Ons kan sien hoe JWT -tokens kompak en baie minder breedvoerig is as hul XML-eweknie, die SAML -tokens. Ons het gesien hoe maklik dit is om tokens te skep en te gebruik om sekere magtigings te kry wat nodig is vir sekere metodes en hoe ons daar kom via 'n getekende token. Ek vind egter baie belangrik om 'n idee te kry van hoe JWT werk. Hopelik het ek hiermee vir jou 'n goeie inleiding gegee oor hoe JWT -tokens werk. Om 'n beter idee te kry van hoe dit alles werk, raai ek jou aan om rond te speel met die geïmplementeerde cypress . Dit is 'n goeie manier om te sien hoe versoeke gemaak word en wat ons toets en wat verwag word. Dan sal jy ook 'n beter idee kry van hoekom sommige gebruikers sekere bewerkings kan uitvoer en ander nie. Ek het al die bronkode van hierdie toepassing op GitHub geplaas. Ek hoop dat jy hierdie artikel soveel geniet het as wat ek dit geniet het om te skryf dit. Dankie dat jy gelees het!

13. Verwysings