paint-brush
Nima uchun JWT korporativ ilovangizni himoya qilishning kalitidir va nima uchun KumuluzEE sizning yangi eng yaxshi do'stingiz bo'lishi mumkintomonidan@jesperancinha
Yangi tarix

Nima uchun JWT korporativ ilovangizni himoya qilishning kalitidir va nima uchun KumuluzEE sizning yangi eng yaxshi do'stingiz bo'lishi mumkin

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

Juda uzoq; O'qish

'JWT' yoki JavaScript Object Notation Web Token [RFC7519] da belgilangan standart bo'lib, u bir necha usul bilan belgilanishi va ikki tomon o'rtasida ma'lumot uzatish uchun ishlatilishi mumkin. Ushbu maqolada biz `JWT`ni umumiy Java korporativ ilovasiga qanday integratsiya qilishni ko'rib chiqamiz.
featured image - Nima uchun JWT korporativ ilovangizni himoya qilishning kalitidir va nima uchun KumuluzEE sizning yangi eng yaxshi do'stingiz bo'lishi mumkin
João Esperancinha HackerNoon profile picture
0-item

Hozirgi kunda bizda ishlash haqida ko'proq tashvishlanmoqdamiz va shu bilan birga, biz tizimlar qanday tez va ishonchli aloqa qilishini bilishni xohlaymiz. Ko'p marta biz ma'lumot yuborishni va uni iloji boricha maxfiy va xavfsiz saqlashni xohlaymiz. Nozik ma'lumotlar ham ba'zan Internetda ommaviy ravishda harakatlanishi va simning boshqa uchida harakatlarni boshlashi kerak. Ko'pincha biz ma'lumotlar mutatsiyalariga olib keladigan harakatlarni yaratmoqchimiz. Bunday hollarda biz nafaqat ma'lumotlarimizni himoya qilamiz. Maʼlumotlarimizni yuborish orqali boshlangan harakatlar ishonchli ekanligiga ishonch hosil qilishni xohlaymiz. Biz ma'lumotlarimizni bir necha usul bilan himoya qilishimiz mumkin. Odatda, biz ma'lumotlarni TLS (Transport Layer Security) xavfsiz ulanishi orqali yuboramiz. Bu bizning ma'lumotlarimiz sim orqali shifrlangan bo'lishini ta'minlaydi. Ikki tomon oʻrtasida ishonchli munosabatlar oʻrnatish va bunga erishish uchun biz sertifikatlardan foydalanamiz.Ushbu maqolada men JWT standartini muhokama qilmoqchiman va JWT qanday qilib umumiy Enterprise ilovasiga integratsiyalash mumkinligini koʻrib chiqmoqchiman. Bu holda, biz KumuluzEE ni ko'rib chiqamiz.Keling, ba'zi asosiy tushunchalarni ko'rib chiqaylik. JWT yoki JSON Web Token, yoki yaxshiroq, JavaScript Object Notation Web Token, RFC7519 da belgilangan standartdir. Ushbu standart, barcha RFC (Izohlar so'rovi) standartlari singari, IETF (Internet Engineering Task Force) tomonidan aniqlangan, yozilgan va nashr etilgan. Uni bir necha usul bilan aniqlash mumkin. Umuman olganda, JWT ikki tomon o'rtasida da'volarni uzatishning ixcham, xavfsiz shakli deb aytishimiz mumkin. Da'vo nima ekanligini soddalashtirishning usullaridan biri uni asosan ma'lumotni o'z ichiga olgan nom/qiymat juftligi sifatida tasvirlashdir. Bizning internet aloqamizning bir necha muhim jihatlarini kafolatlash uchun bizga bu ma'lumot kerak. Biz olgan ma'lumotlar birinchi navbatda tasdiqlangan va ishonchli ekanligiga ishonch hosil qilishimiz kerak. Keyin biz buni tasdiqlashimiz kerak. Bu asosan shunday. Ushbu standartni amalga oshirish uchun biz Java korporativ ilovasini amalga oshirishga yordam beradigan bir nechta ramkalardan foydalanishimiz mumkin. Spring Boot keng qo'llaniladi. Ko'pincha u banklar va boshqa moliyaviy tashkilotlar kabi ba'zi tashkilotlarning tegishli dasturiy ta'minotida boshqa nom ostida o'ralgan. Bizning misolimiz uchun men boshqa narsa qilishga qaror qildim. Spring Boot o'rniga biz KumuluzEE bilan misolni ko'rib chiqamiz. Gap JWT nima ekanligini va uning qanday ko'rinishini aniq aniqlashdir. Java Enterprise ilovalari asosan dastur serverida joylashtiriladigan yoki o'rnatilgan server yordamida o'z-o'zidan ishlashi mumkin bo'lgan ilovalardir. Misol tariqasida, Spring Boot ilovalari o'rnatilgan Tomcat serverida ishlaydi. Ushbu maqolada bizning e'tiborimiz KumuluzEE ga qaratiladi. Spring Boot singari, u ham o'rnatilgan serverni o'z ichiga oladi. Bundan tashqari, bu holda u Jetty deb ataladi. Bu CDI (Context Dependency Injection) ni ta'minlash uchun Weld bilan birgalikda ishlatiladi. Barcha Java EE va Jakarta EE texnologiya standartlari ushbu framework bilan mos keladi.

2. Hodisaga misol


JWT asosiy shaklida qanday ishlashini misol qilish uchun men uni taqdim etish usulini o'ylab ko'rishim kerak edi. Xavfsizlik muammosi bo'lgan klassik misollar banklardir. Biroq, JWT qanday ishlashini ko'rsatish uchun to'liq bank arizasini tayyorlash vaqtni behuda sarflash va ehtimol juda ko'p tushunchalarni o'z ichiga olishi mumkin. Buning o'rniga, men qilgan narsa juda oddiy bank tizimi. Bizning asosiy tashvishimiz ma'lumotlar sim orqali qanday oqishini va foydalanuvchilar ilovamizning muayyan sohalariga qanday kirishlarini ko'rsatishdir. Men shuningdek, TLS yoki shifrlangan ma'lumotni sim orqali qanday yuborishimiz mumkinligini muhokama qilmoqchi emasman. Biz JWT e'tiborimizni sof shaklda beramiz. Bizning ishimiz tabiat va atrof-muhitni himoya qiluvchi guruh tomonidan foydalaniladigan bank tizimidir. Bu JWT qanday ishlashini ko'rsatishning qiziqarli usuli. Ushbu Tabiat Ligasining bosh qahramoni Lyusi bo'lib, u mening barcha maqolalarimda umumiy xarakterga aylanib bormoqda.

3. Arxitektura

Ishni boshlashdan oldin, keling, ishlayotgan dasturimizni chizamiz. Bu juda oddiy dastur, lekin uni chizish hali ham yaxshi narsa:

Bu juda oddiy bo'lishining sababi shundaki, JWT har bir so'rov bo'yicha tekshiriladi va har bir so'rov ochiq kalit bilan tekshiriladi, shuning uchun biz bilamizki, har bir so'rov bo'yicha to'g'ri tokenni yuborsak, biz buni amalga oshirishimiz mumkin. JWT OAuth2, Okta SSO yoki boshqa avtorizatsiya mexanizmi bilan birlashtirilishi mumkin. Bunday holda, biz qilayotgan narsa autentifikatsiya va avtorizatsiyani o'rnatishdir. Ilovamizda biz JWT foydalanamiz va u bilan imzo yordamida xabarimizni autentifikatsiya qilamiz. Ammo biz ilovaga kirmaymiz. Buning o'rniga, muvaffaqiyatli autentifikatsiyadan so'ng foydalanuvchilarga ilovamizdan foydalanishga ruxsat beramiz. Shu nuqtada, JWT aslida to'liq dasturning juda kichik qismi ekanligini ko'rish oson. Shunga qaramay, ba'zi funksiyalarni qo'shish kerak. Bular bizga kerak bo'lgan manbalar:

  • Balans tizimi
  • Kredit tizimi


Aytaylik, bizning asosiy tizimimiz faqat pul va kredit so'rovlarini ro'yxatga oladi. Aslida, u faqat qiymatlarni to'playdi. Aytaylik, ba'zi odamlar kredit olishlari mumkin, boshqalari esa yo'q. Ba'zi odamlar pul saqlash imkoniyatiga ega bo'ladi va boshqalar kredit olish imkoniyatiga ega bo'ladi.

4. Texnologiyalarni tanlash

Kirish qismida aytib o'tilganidek, biz KumuluzEE korporativ dastur tizimi sifatida foydalanamiz va JWT asosiy terminologiyasi va tushunchalarini ko'rib chiqishimiz mumkin bo'lgan tarzda ultra-asosiy dasturni amalga oshiramiz. Java versiyasining to'g'ri ekanligiga ishonch hosil qiling. Ushbu bosqichda bizga minimal Java 17 SDK o'rnatilgan bo'lishi kerak. Bizga maven, git, IntelliJ kabi Java-mos keluvchi IDE va qandaydir qobiq kerak bo'ladi.

5. Sozlash

Ilovamizni boshlash uchun bizda bir nechta KumuluzEE bog'liqliklari mavjud. Buning sababi, KumuluzEE xuddi Spring Boot kabi bir nechta qaramlikka muhtoj. Keling, POM faylini qisqacha ko'rib chiqaylik:

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

Keling, bir nechta bog'liqliklarni qisqacha muhokama qilaylik. Buni o'qiyotganingizda, iltimos, pom.xml faylimizni yuqoridan pastgacha kuzatib boring. Bu quyidagi tushuntirishni tushunish uchun muhim. Ilovamiz ishlashi uchun bizga bog'liqliklar to'plami kerak. , Yaxshiyamki, KumuluzEE bizga ushbu ilovani ishga tushirish uchun asosiy standart to'plamlarni o'z ichiga olgan Mikroprofil kutubxonalarini taqdim etadi. Bularning barchasi KumuluzEE -Microprofile kutubxonasida mavjud. Ilovamizni barcha kerakli JWT parametrlari bilan sozlash uchun biz unga MicroProfile kutubxonasini qo'shishimiz kerak. Shu bilan birga, bizga JSON ishlov berish kutubxonasi kerak. Bu Jonson Core qiladigan narsa bo'ladi. Bizga, albatta, ishlash uchun KumuluzEE yadrosi kerak. Jetty - bu KumuluzEE ramkasini boshqaradigan asosiy server. Shuning uchun bu bizning qaramligimizda kerak. Bizga CDI kerakligini hisobga olsak, uni qo'llab-quvvatlaydigan kutubxona ham kerak. REST so'nggi nuqtalarimizni yoqish uchun bizga KumuluzEE ning qolgan kutubxonasi kerak. API-ni olish uchun bizga Geronimo kutubxonasi kerak bo'ladi. Bu bizda JSR-374 ilovasi mavjudligini ta'minlaydi. Biz JWT va uning JSON-formatted mazmunini ham izohlashimiz kerak. Lombok o'z-o'zidan kerak emas. Bu shunchaki hamma narsani chiroyli va yorqin qiladi! Jurnallarni yaxshiroq talqin qilish va natijalarimizni tushunishimiz uchun tizimga qaytish ham muhim. Endi resources papkamizni ko'rib chiqamiz. Boshlash uchun avval ushbu jildda nimani topishimiz kerakligini tushunib olaylik. Biz ilovamizni JWT , Logback bilan bog'liq bo'lgan narsa bilan sozlashimiz kerak va nihoyat, biz yaratmoqchi bo'lgan fasol haqida nimadir aytishimiz kerak. U erda eng oddiy faylni ko'rib chiqamiz. beans.xml faylini META-INF da topish mumkin:

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

Bu oddiy va siz hozir o'ylayotganingizdek, biroz eski fayl. Shu nuqtada, g'oya faqat KumuluzEE ishga tushirishdir. Bizda istisno amali bor. Bu Weldga loviya harakatini skanerlashda hisoblar sinfini hisobga olmaslikni aytadi. Bu juda muhim, chunki biz foydalanayotgan dastur bilan Weld asosan bo'sh konstruktorli har bir sinfni loviya sifatida ko'rib chiqadi. Nima uchun Hisoblar loviya hisoblanishini istamasligimizni keyinroq bilib olamiz. Hozircha shuni yodda tutaylikki, biz so'rovlar doirasida so'rovlar yubormoqdamiz. Bu mantiqan to'g'ri, chunki har bir so'rov boshqa foydalanuvchiga ega bo'lishi mumkin. Endi " logback " qanday amalga oshirilishini ko'rib chiqamiz. U META-INF da topilgan:

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

Bu bizning logs uchun juda oddiy konfiguratsiya. Nihoyat, ilovamizning eng muhim fayli. Bu konfiguratsiya shablonidir. Shu o‘rinda shuni ta’kidlash kerakki, men ushbu loyihada yaratgan ba’zi fayllar shablon tuzilishining bir qismidir. Bu haqda keyinroq batafsil tushuntiraman. Ushbu shablon fayli MicroProfile tomonidan o'qiladigan config.yml fayliga aylantirilishi kerak. Ushbu fayl resurslarning ildizida joylashgan:

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

Bu xususiyatlarning barchasi aslida nimani anglatishini keyinroq ko'rib chiqamiz. Ularning barchasi o'z-o'zidan tushunarli. PublicKey va emitent almashtiriladigan barcha parametrlardir. Biz buni keyinroq o'rganamiz. Bizning bash skriptlarimiz ularning almashtirilishiga ishonch hosil qiladi. Biz kodlashga deyarli tayyormiz, lekin avval JWT token tuzilishini ko'rib chiqamiz.

6. Amaliy kod

Keling, juda kichik dasturimizni yarataylik. Ushbu bo'limda ilovamizni JWT bilan ishlash uchun qanday qilib olishimiz mumkinligi tushuntiriladi. Biz ko'rmoqchi bo'lgan narsa, foydalanuvchilarga boshqalarga emas, balki ba'zi REST usullarimizga kirishni belgilashimiz mumkin. Ushbu kodni ko'rib chiqishni boshlash usullaridan biri, avvalo oddiy JWT tokenimizni ko'rib chiqishdir. Mana bizning administrator misolimiz:

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

JSON ushbu nomlarning har biri da'volar deb ataladi. Bizning misolimizda biz bir nechta himoyalangan da'volarni ko'ramiz:

  • " iss " - bu tokenning emitenti. Buning uchun o'zboshimchalik bilan qiymat tanlashimiz mumkin. Ushbu parametrning qiymati biz ilgari ko'rgan config.yml da almashtiriladigan emitent o'zgaruvchisiga mos kelishi kerak.
  • " jti " - Bu tokenning noyob identifikatori. Masalan, biz ushbu da'voni token ikki yoki undan ortiq marta ishlatilishini oldini olish uchun ishlatishimiz mumkin.
  • " sub " - Bu token mavzusi. Bu foydalanuvchi yoki bizga yoqadigan narsa bo'lishi mumkin. Shuni yodda tutish kerakki, bu identifikator, kalit, nomlash yoki biz xohlagan narsa sifatida ham ishlatilishi mumkin.
  • " upn " — foydalanuvchining asosiy ismi. Bu foydalanuvchi foydalanadigan asosiyni aniqlash uchun ishlatiladi.
  • " groups " - Bu joriy foydalanuvchi tegishli bo'lgan guruhlar massivi. Asosan, bu token bilan so'rov nima qilishi mumkinligini aniqlaydi. Tokenimizda biz bir nechta Maxsus daʼvolarni koʻramiz. Biz bundan xuddi Zaxiralangan daʼvolar kabi foydalanishimiz mumkin
  • " user_id " - Biz bundan foydalanuvchi identifikatorini o'rnatish uchun foydalanamiz.
  • " access " — Biz foydalanuvchining kirish darajasini aniqlaymiz.
  • " name " - foydalanuvchi nomi.

7. Amaliy kod

Keling, hozirgacha bilganlarimizni takrorlaylik. Biz belgilagan tuzilishga ega bo'lgan tokenlar bilan aloqa qilishimizni bilamiz. Bundan tashqari, biz ilovamiz konfiguratsiyasini, logback konfiguratsiyasini o'rnatdik va nihoyat, korporativ loviya qidirish uchun maxsus konfiguratsiyani o'rnatdik. Keling, paket modelini ko'rib chiqaylik. Bu erda biz 3 ta sinfni topamiz. Bu sinflar asosan hisoblar yig'indisini va client va account o'rtasidagi vakillikni ifodalaydi. Shunday qilib, Client joylashgan Model.kt kotlin fayliga qarashdan boshlashimiz mumkin:

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

Ushbu birinchi model klassi bizning mijozimiz vakilidir. Bizning ishimiz uchun client faqat ismga ega. Bu " jwt " atribut nomi bilan ifodalangan foydalanuvchi nomi. Bundan tashqari, bizda Account bor:

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


Ushbu sinfda biz asosan hisob raqamini, mijozni, joriy qiymatni va nihoyat kredit qiymatini o'rnatamiz. E'tibor bering, biz barcha qiymatlarni sukut bo'yicha 0 ga qo'yamiz. Biz BigDecimaldan ham foydalanamiz, chunki biz pul bilan ishlaymiz. Pul aniq bo'lishi kerak va tizimning o'zgarishi yoki pastga tushishiga duch kelmasligi kerak. Bu boshqa so'zlar bilan aytganda va misol sifatida 0. 0000000000000000000000000000000000000000000000000001 evro kabi raqam har doim shu raqam bo'lib qolishi kerakligini anglatadi. Shuningdek, biz hisobimizga qiymatlar qo'shmoqchimiz. Bu erda addCurrentValue usuli paydo bo'ladi. Xuddi shu sabablarga ko'ra, biz ham kreditimizni addCreditValue bilan to'ldiramiz.Nihoyat, ma'lumotlar sozlamalarimizning oxirgi qismida biz Accounts sinfiga duch kelamiz:

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

Bu aslida barcha hisoblarimizning yig'uvchisidir. Biz uning xarita mazmunidan maʼlumotlar bazasining harakatini taqlid qilish uchun foydalanamiz. Endi kontroller paketini koʻrib chiqamiz. Bu erda biz ma'lumotlar modelimiz bilan ishlaydigan ilovamizni yaratamiz. Birinchidan, BankApplication sinfini ko'rib chiqaylik:

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


Bu bilan biz 3 ta muhim narsani aytamiz. LoginConfig izohi bilan biz uni MicroProfile-ga muvofiq JWT tokenlaridan foydalanish va tushunish uchun belgilaymiz. ApplicationPath ilova ildizini belgilaydi. Bu erda ilovaning URL manzili boshlanadi. Bizning misolimizda u HTTP://localhost:8080 bo'ladi. Va nihoyat, DeclareRoles ilovamiz tomonidan ishlatiladigan va qabul qilinadigan rollarni belgilaydi. Rollar va guruhlar bu vaziyatda bir-birini almashtiradigan atamalardir. Inyeksiya samarali ishlashi uchun biz hisob xaritasini aniqlash uchun maxsus izoh yaratamiz:

 annotation class AccountsProduct

To'liq ekran rejimiga kirish To'liq ekran rejimidan chiqish

Keyinchalik, AccountsFactory kesh ob'ektini yaratamiz:

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


Aynan shu zavod Accounts uchun qidiruvni o'chirib qo'yganimiz sababidir. Qidiruv jarayoniga fasol yaratishga ruxsat berish o'rniga, biz o'zimiz agregator misolini yaratamiz. Produces izohidan foydalanish bizga loviya yaratish imkonini beradi. Bizning shaxsiy izohimiz, AccountsProduct yordamida biz ushbu loviyadan foydalanishni yanada aniqroq qilamiz. Nihoyat, ApplicationScoped foydalanib, biz uning doirasini Application doirasi sifatida aniqlaymiz. Boshqacha qilib aytadigan bo'lsak, hisobni yig'ish loviya ilova bo'ylab yagona ob'ekt sifatida ishlaydi. " createResponse " JSON javoblarini yaratishning oddiy usulidir. Hozir bizga ikkita "Resurs" kerak. Bu asosan bahorda " Controllers " bilan bir xil. Bu boshqa nom, lekin aynan bir xil foydalanishga ega. Keling, AccountsResource sinfini ko'rib chiqaylik:

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

Ushbu sinfni batafsil ko'rib chiqish uchun bir lahzani ajrating. Path izohi ushbu manbaga ildizdan qanday erishish mumkinligini belgilaydi. Esda tutingki, biz "/" dan ildiz sifatida foydalanamiz. Bunday holda, "hisoblar" bu resurs uchun bizning asosiy kirish nuqtamizdir. Bizning barcha resurslarimiz, bizning holatlarimizda faqat ikkitasi RequestResource bilan ishlaydi. Annotatsiya bilan Produces barcha so'rovlarga javoblar turidan qat'iy nazar JSON formatidagi xabarlar ko'rinishida bo'lishini aniqlaydi. aggregator kiritish uchun biz faqat Inject annotatsiyasi va AccountsProduct izohi birikmasidan foydalanamiz:

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


Bu biz zavodda belgilagan narsaga mos keladi. Bundan tashqari, biz xavfsizlikning ikkita muhim elementini ham kiritamiz. principal va jsonWebToken :

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


JsonWebToken ham, Principal ham bir xil bo'ladi va biz buni jurnallarimizda ko'ramiz. Resurslarimizda biz har doim ma'lum bir token bilan so'rovdan da'volarni kiritishimiz mumkin:

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


Bu Inject va Claim izohlarining kombinatsiyasi bilan amalga oshiriladi. Claim izohi ostidagi nom qaysi daʼvo kiritmoqchi ekanligimizni belgilaydi. Biz parametrlarimizni belgilagan turga ehtiyot bo'lishimiz kerak. Bizning misolimizda, r bizga faqat JsonString va JsonNumber turlari kerak. Birinchidan, hisoblar va foydalanuvchilarni qanday yaratayotganimizni ko'rib chiqamiz:

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

Hisob qaydnomalari va foydalanuvchilarni yaratish


Bu erda maqsad usullarni ajratish va ularga turli ruxsatnomalar berishdir. Bizning misolimizda, ularning ikkalasi ham shunchaki hisob yaratadi, lekin shuni ta'kidlash kerakki, faqat foydalanuvchi roli bo'lgan foydalanuvchilar createUser usulidan foydalanishi mumkin. Xuddi shu tarzda, faqat mijoz va kredit roliga ega foydalanuvchilar createAccount usulidan foydalanishlari mumkin. Endi ushbu resursning PUT so'rov usulini batafsil ko'rib chiqamiz:

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

Naqd pul olish


Biz bilamizki, PUT izohi ushbu usulga faqat PUT tipidagi so'rovlar bilan kirish mumkinligini bildiradi. Annotatsiya yo'li keyin Jettyga ushbu usulga yo'l qiymat ekanligini aytadi. Bu PathParam sifatida ham tanilgan. Nihoyat, biz ushbu usulni faqat administrator yoki mijoz roliga ega foydalanuvchilar ishlatishiga ruxsat berishimiz mumkin. Kirish qiymati PathParamdan foydalanish orqali bizning Long qiymat o'zgaruvchimizga o'tkaziladi. Agar biz hech qanday rollarni belgilamasak, to'g'ri tokenga ega har qanday foydalanuvchi ushbu usullardan foydalana oladi. CreditResource xuddi shu tarzda amalga oshiriladi. yo'l:

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

Yagona farq shundaki, biz admin va client rollarini ishlatish o'rniga endi admin va credit rollaridan foydalanmoqdamiz. Bundan tashqari, ushbu resource foydalanuvchilar hisoblari hech qachon yaratilmasligiga e'tibor bering. Bu faqat hisob resource orqali mumkin. Endi kod qanday amalga oshirilganligini bilganimizdan so'ng, avval REST xizmatimizda qaysi usullarni taqdim etganimizni takrorlaymiz.

8. Ilovadan foydalanish

Keling, foydalaniladigan xizmatlar ro'yxatini ko'rib chiqaylik:


Tur, URL, Yuk, Natija, Ruxsat etilgan rollar
POST, http://localhost:8080/accounts,n/a,Yaratilgan hisob, administrator/mijoz/kredit
POST,
http://localhost:8080/accounts/user,n/a,Yaratilgan foydalanuvchi, admin/foydalanuvchi
OLISH,
http://localhost:8080/accounts,n/a,Mos hisob, admin/mijoz
QO'YISH,
http://localhost:8080/accounts,{saldo: Long}, Joriy balans, admin/mijoz
OLISH,
http://localhost:8080/accounts/all,n/a,Hammasi joriy hisoblar, Hammasi
OLISH,
http://localhost:8080/accounts/summary,n/a,Sum barcha balanslar, Hammasi
OLISH,
http://localhost:8080/credit,n/a,Mos keladigan hisob, admin/mijoz
QO'YISH,
http://localhost:8080/credit,{saldo: Long}, Joriy kredit, admin/mijoz
OLISH,
http://localhost:8080/credit/all,n/a,Hammasi kreditlar, Hammasi
OLISH,
http://localhost:8080/credit/summary,n/a,Sum
kreditlar, Hammasi

9. Test muhitini yaratish

Men ildiz papkasida bash faylini yaratdim. Ushbu fayl "setupCertificates.sh" deb ataladi. Keling, nima qilishini tushunish uchun uni ko'rib chiqaylik:

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

Atrof muhitni yaratish

Iltimos, faylga amal qiling, chunki men nima qilishini tushuntiraman. Bu nima qilayotganini aniq tushunishimiz uchun muhim. Biz birinchi navbatda PEM formatida shaxsiy va ochiq kalitlarni yaratamiz. Keyin biz shaxsiy kalitni ishga tushiriladigan "your-finance-jwt-generator.jar" bilan ishlatamiz. Bu tokenlarni tezda yaratishga imkon beruvchi bizning ishlaydigan idishimiz. Emitentni keyinchalik o'zgartirib bo'lmaydi. Nihoyat, u token yaratadi. Ushbu tokenni qanday o'qishni keyinroq ko'rib chiqamiz. Bu tokenda 3 ta qoʻshimcha sarlavha daʼvolari mavjud. Bular "bolalar", "typ" va "alg". U quyidagi formatga amal qiladi:

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

JWT sarlavhasi

Keling, ushbu da'volarni batafsil ko'rib chiqaylik:

  • "Bola" - maslahat da'vosi sifatida ishlaydi. Bu biz qaysi turdagi algoritmdan foydalanayotganimizni ko'rsatadi.
  • "typ" - IANA media turlarini e'lon qilish uchun ishlatiladi. Uchta variant mavjud: JWT (JSON Web token), JWE (JSON Web Encryption) va JWA (JSON Web Algoritms). Bu turlar bizning tajribamizga tegishli emas. Biz faqat bizning tokenimiz juda yaxshi shifrlanmaganligini va uni parolini hal qilish juda oson ekanligini ko'ramiz. Shuningdek, biz tokenlarning shifrini ochishimiz mumkin bo'lsa-da, boshqa amallarni bajarish uchun ularni osonlikcha o'zgartira olmasligimizni ko'ramiz.
  • "alg" - Biz foydalanmoqchi bo'lgan imzo turini shunday aniqlaymiz. Imzolanishni kriptografik operatsiya deb hisoblash mumkin, bu asl token o'zgartirilmaganligini va ishonchliligini ta'minlaydi. Bizning holatda, biz SHA-256 bilan RSA imzosi deb nomlanuvchi RS256 dan foydalanamiz.

Ochiq kalitimiz yordamida biz nihoyat shablonimizni o'zgartirish uchun foydalanishimiz mumkin. Yangi config.yml fayli quyidagicha ko'rinishi kerak:

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

config.yml


Ikkinchi qadam to'rtta fayl yaratishdir. " jwt-plain-tokens " katalogidagi har bir oddiy token uchun biz to'rtta buyruq yaratamiz. Birinchi buyruq o'z hisoblari bilan samarali ishlarni qila oladigan foydalanuvchilarni yaratishdir. Bular " admin ", " client " va " credit " profillariga ega foydalanuvchilar. Ularni yaratish uchun " createAccount.sh " faylini ishga tushiramiz. Ikkinchi buyruq hali hech qanday huquqlarga ega bo'lmagan qolgan foydalanuvchilarni yaratadi. Bu "createUser.sh" fayli. Keling, ishga tushiraylik. Endi biz barcha foydalanuvchilar nihoyat yaratilganligini ko'ramiz. Endi tranzaktsiyalar tafsilotlarini ko'rib chiqamiz va qolgan ikkita buyruqni ko'rib chiqamiz. Biri "kashin" qilish uchun, ikkinchisi esa ko'proq kredit so'rash uchun. Birinchi yaratilgan fayl "sendMoney.sh" bosh skriptidir. Bu erda biz " cashin " bo'yicha barcha so'rovlarni topishingiz mumkin. Ushbu faylda siz har bir foydalanuvchiga tasodifiy pul miqdorini yuborish uchun jingalak so'rovini topasiz. Keling, administrator ishini ko'rib chiqaylik:

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

Xuddi shu foydalanuvchilarning kredit so'rovlari ham ularga tayinlangan:

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



Bizning barcha characters Nature Ligasining bir qismidir. Aslida, bu bank tizimining bir qismi bo'lishi uchun faqat bir guruh odamlar. Shu nuqtai nazardan, ular atrof-muhitni himoya qilmoqdalar. Bu odamlar guruhi nima qilayotgani yoki hikoyaning qayeriga mos kelishi maqola uchun unchalik muhim emas, lekin kontekst uchun ular atrof-muhitni himoya qilish va iqlim o'zgarishi oqibatlarini sekinlashtirish bo'yicha harakatlarda qatnashadilar. Ba'zi characters hamma narsani qila oladi, boshqalari hech narsa qila olmaydi, boshqalari esa faqat "kashin" yoki "kredit so'rashi" mumkin. Bundan tashqari, men nozik ma'lumotlarni chalkashtirib yuborayotganimga e'tibor bering. Ushbu tokenlar odatda baham ko'rilmasligi yoki ma'lum bir URL manzilida ko'rinmasligi kerak. Ha, ular har doim brauzerni ishlab chiquvchi konsoli orqali mavjud, ammo baribir ba'zi so'rovlarni protect uchun. Bu "xavfsizlik-noaniqlik" deb nomlanuvchi tushuncha bo'lib and foydalanuvchining foydalanilayotgan tokendan xabardor bo'lishiga texnik jihatdan to'sqinlik qilmasa ham, u to'xtatuvchi vosita sifatida ishlaydi. Ikkala usulda ham, biz depozit qo'yganimizda yoki biz kredit so'rang, e'tibor bering, har bir so'rov uchun biz 1 dan 500 gacha bo'lgan tasodifiy raqamni yubormoqdamiz.

10. JWT tokeni qanday yasaladi




Endi biz tokenlarimizni yaratdik, keling, ulardan birini ko'rib chiqaylik. Men sizga tushunarsiz tokenni ko'rsatmoqchiman va biz buni tushunish uchun undan foydalanamiz. Mana bizning tokenimiz: FAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKE . FAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETO . FAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKENFAKETOKEN Shuni ta'kidlash kerakki, bizning tokenimiz uch qismga bo'lingan:

  • Sarlavha - Bu yuqorida muhokama qilganimizdek, Base64 kodlangan JSON konfiguratsiya sarlavhasi.
  • Foydali yuk - Bu Base64 kodlangan JSON foydali yukidir. Bu yerda biz Zahiralangan va Shaxsiy daʼvolarimizni aniqladik. Shuningdek, biz bu erda Xususiy va Davlat da'volarini aniqlashimiz mumkin. Ularning ikkalasi ham maxsus da'volar ostida. Tezkor eslatma sifatida biz ushbu ikkala da'vo bilan xohlagan narsani qila olamiz. Biroq, ommaviy da'volar IANA JSON Web Token reestrida belgilanganlar deb ataladi. Ro'yxatga olish kitobi bilan to'qnashuvlarga yo'l qo'ymaslik uchun tokenlarimizni ma'lum bir tarzda nomlashimiz muhimdir. Ommaviy da'volar standart ixtiyoriy sifatida ham belgilanishi mumkin. Xususiy da'volar hech qanday me'yorga amal qilmaydi va ularni aniqlash bizga bog'liq.
  • Imzo - Bu erda biz biroz ijodiy bo'lishimiz mumkin. Imzo Header va Payload shifrlangan birikmasidir. Biz foydalanmoqchi bo'lgan algoritmni hal qilamiz va tokenning bu qismi asosan biz yuborayotgan xabarga ishonish yoki yo'qligini aniqlaydi. Bu o'sha kombinatsiyaga xosdir va serverimiz mos keladigan yoki yo'qligini aniqlash uchun biz yaratgan "ommaviy kalit" dan foydalanadi. Yuqoridagilarni eslasangiz, biz misolimizda RS256 foydalanamiz.


Davom etishdan oldin, iltimos, bizning misolimizda Header va Payload decyphered mumkinligini unutmang. Biz shunchaki foydali yuk yoki sarlavhani o'zgartira olmaymiz va baribir uni ishonchli qilamiz. Zararli tokenning potentsial ta'siridan himoya faqat biz tanlagan algoritm bilan himoyalanishi mumkin. Shuning uchun oqilona tanlang. Agar siz bank kabi o'ta maxfiy ma'lumotlar tashvishga soladigan tashkilotda ishlayotgan bo'lsangiz, iltimos biz qilmoqchi bo'lgan ishni QILMANG. Bu biz uchun mahalliy ishlab chiqarilgan tokenlar tarkibini onlayn tekshirishning bir usuli. Birinchidan, https://jwt.io/ saytiga o‘tamiz va JWT tokenimizni to‘ldiramiz. O'zingiz yaratgan tokendan foydalaning:


Tokenimiz tarkibini tekshirish uchun https://jwt.io/ dan foydalaning, keling, bizda nima borligini ko'rib chiqaylik. Bu bizning administrator tokenimiz. Bu odam bizning misolimizda "Admin". Bizning barcha parametrlarimiz mavjudligini ko'rishimiz mumkin. Ro'yxatimizda "sub", "aud", "upn", "access", "user_id", "iss", "name", "groups" va nihoyat "jti" ni ko'ramiz. Bizda qo'shimcha da'volar ham bor. Keling, ularni ko'rib chiqaylik:



" auth_time " - Bu autentifikatsiya sodir bo'lganda. Bizning tokenimiz 2022-yil 17-iyul, yakshanba kuni 16:15:47 GMT+02:00 DST" iat "da autentifikatsiya qilingan - Bu token yaratilgan. Bizning holatda, bu auth_time bilan bir vaqtda sodir bo'ladi." exp " — Bu tokenning amal qilish muddati. 2022-yil 17-iyul, yakshanba kuni 16:32:27 GMT+02:00 DST tugaydi. Tokenimizda hech qanday amal qilish muddati ko‘rsatilmagan. Bu shuni anglatadiki, JWT o'zining ~15 daqiqalik standart qiymatidan foydalanadi.

Keling, ba'zi testlarni bajaramiz.

11. Ilovani ishga tushirish

Kod GitHub da foydalanishga tayyor. Agar biz kodni tekshirib chiqsak va uni Intellij bilan ochsak, bu dasturni Spring Boot ilovasi kabi ishga tushira olmasligimizni bilishimiz kerak. Uni ishga tushirish uchun "psvm" yo'q. Buning o'rniga, biz yaratilgan kavanozni to'g'ridan-to'g'ri ishga tushirishimiz va bundan oldin "mvn qurish" ni yaratishimizga ishonch hosil qilishimiz mumkin. Hozir men uni qanday ishlataman:

[ ] https://github.com/jesperancinha/your-finance-je "Ilovani ishga tushirish uchun muhitni sozlash")



Endi “ setupCertificates.sh ” skriptini qayta ishga tushiramiz. Bu yerga kelish uchun qancha vaqt ketganingizni bilmayman, lekin 15 daqiqa allaqachon o'tib ketgan bo'lishi mumkin. Har holda, ularni qayta ishga tushiring. Keling, ilovamizni ishga tushiramiz! Uni quyidagicha boshlashimiz mumkin:

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

Yoki biz uni ishga tushirishga tayyor konfiguratsiyamiz orqali ishga tushirishimiz mumkin. Agar hamma narsani tushunmoqchi bo'lsangiz, repo va Makefile-ni oldindan tekshiring:

 make dcup-full-action

Ushbu skript 2 ta xizmatni ishga tushiradi. Biri 8080 portida, ikkinchisi 8081 portida. 8080 portida biz JWT tokenlarini yaratish uchun o'z kodimiz bilan ishlaydigan ushbu dasturning versiyasini ishga tushiramiz. 8081 portida Adam Bien tomonidan yaratilgan jwtknizr generatoridan foydalangan holda versiyani ishga tushiramiz. Biz ushbu maqolani 8080 portida ishlaydigan xizmatga qaratamiz. Agar xohlasangiz, cypress bilan ham ishlatishingiz mumkin:

 make cypress-open

Bu cypress konsolini open va siz tanlagan brauzer yordamida testlarni o'tkazishingiz mumkin bo'ladi. Biroq, ushbu bosqichda brauzer imkoniyatlari hali ham cheklangan. So'rovlarning aksariyati aslida cypress tomonidan taqdim etilgan buyruq qatori so'rovlari bo'ladi. Hozircha " cypress " ga kirmaylik. Iltimos, brauzeringizga o'ting va ushbu manzilga o'ting:

http://localhost:8080/accounts/all

Biz shunday natijaga erishishimiz kerak:


Ko'rib turganimizdek, " Malory ", " Jack Fallout " va " Jitska " ning krediti yoki puli yo'q. Buning sababi, ularga faqat foydalanuvchi guruhi berilgan. Shuningdek, Shikka kredit berilmaganiga e'tibor bering. " Shikka ", bizning guruh kreditiga ega bo'lmagan yagona mijozimiz. Agar jurnallarga qarasak, muvaffaqiyatli operatsiyalar quyidagi formatga ega ekanligini ko'rishimiz mumkin:

 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 bizga operatsiya muvaffaqiyatli o'tganligini bildiradi. "Malory", "Jek Fallout" va "Jitska" holatlarida ikkala operatsiya ham muvaffaqiyatsiz tugadi va keyin biz bunday xabarni olamiz:

 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 JWT tokenimiz tasdiqlanganligini va ishonchli ekanligini bizga bildiradi. Biroq, foydalanuvchiga ushbu operatsiyani bajarish taqiqlanadi. Boshqacha qilib aytganda, ular belgilangan usulga kirish imkoniga ega emaslar.

Keling, tokenlarimizni biroz o'zgartiraylik. Agar sendMoney.sh faylining ba'zi belgilarini o'zgartirsak. Biz buni olishimiz kerak:

 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)

To'liq ekran rejimiga kirish To'liq ekran rejimidan chiqish

Bu 401 bizning tokenimiz tasdiqlanmaganligini anglatadi. Bu shuni anglatadiki, server bizning tokenimiz ishonchli yoki yo'qligini tekshirish uchun foydalanadigan ochiq kalit mos kelmadi. Agar ochiq kalit JWT tokenining imzosini baholay olmasa va tasdiqlay olmasa, u keyin uni rad etadi.

Xulosa qilib aytganda, Sarlavha va "Payload" shifrlanmagan. Ular faqat 64 ta "kodlangan". Bu shuni anglatadiki, "Dekodlash" har doim foydali yuk nima ekanligini ko'rishga imkon beradi. Agar biz foydali yukimizni tinglashdan himoya qilmoqchi bo'lsak, tokenning "Yuqori" dan identifikatsiya parametrlarini tanlashdan boshqa narsa uchun foydalanmasligimiz kerak. Muammo haqiqatan ham kimdir JWT tokenini qo'liga olganida, masalan, TLS tunneli buzilganida va kimdir almashilgan xabarlar mazmunini o'qiy olsa. Bu sodir bo'lganda, yana bir himoya mavjud. Va bu imzo. Kirayotgan xabarni tekshirishga qodir bo'lgan yagona narsa ochiq kalitni o'z ichiga olgan serverdir. Ushbu ochiq kalit, garchi ochiq bo'lsa-da, faqat imzo va "Sarlavha + Payload" bilan ishlash orqali kiruvchi xabarni tasdiqlash imkonini beradi.

12. Xulosa

Biz sessiyamizning oxiriga yetdik. Bunga rioya qilganingiz uchun tashakkur. Biz JWT tokenlarining XML hamkasbi SAML tokenlariga qaraganda qanchalik ixcham va juda kam batafsil ekanligini ko'rishimiz mumkin. Biz ma'lum usullar uchun zarur bo'lgan ruxsatlarni olish uchun tokenlarni yaratish va ulardan foydalanish qanchalik oson ekanligini va imzolangan token orqali u erga qanday etib borishimizni ko'rdik. Ammo JWT qanday ishlashi haqida tasavvurga ega bo'lish juda muhim deb hisoblayman. Umid qilamanki, bu bilan men sizga JWT tokenlari qanday ishlashi haqida yaxshi ma'lumot berdim. Bularning barchasi qanday ishlashi haqida yaxshiroq tasavvurga ega bo'lish uchun sizga amalga oshirilgan cypress sinovlari bilan o'ynashni maslahat beraman. Bu so'rovlar qanday amalga oshirilayotganini, biz nimani sinab ko'rayotganimizni va nima kutilayotganini ko'rishning ajoyib usuli. Shunda siz nima uchun ba'zi foydalanuvchilar ma'lum operatsiyalarni bajarishi, boshqalari esa bajarmayotgani haqida ham yaxshiroq tasavvurga ega bo'lasiz. Men ushbu ilovaning barcha manba kodini GitHub'da joylashtirdim. Umid qilamanki, bu maqolani yozish men kabi sizga ham yoqdi. o'qiganingiz uchun tashakkur!

13. Adabiyotlar


L O A D I N G
. . . comments & more!

About Author

João Esperancinha HackerNoon profile picture
João Esperancinha@jesperancinha
Software Engineer for 10+ Years, OCP11, Spring Professional 2020 and a Kong Champion

TEGI QILISH

USHBU MAQOLA TAQDIM ETILGAN...