Spring Security és Spring Boot

Technológiák: Spring Security 6, Spring Boot 3, Thymeleaf, Spring Data JPA, H2

(Azért írtam meg ezt a posztot, mert sokan kerestek a Spring Security-re, továbbá 2022 novemberében kijött a Spring Security 6 és Spring Boot 3, valamint teljesértékű Spring Security poszt Spring Boottal eddig hiányzott. A Spring Security használata Springgel Frameworkkel poszt továbbra is elérhető.)

A Spring Security egy olyan keretrendszer, mely támogatja az autentikációt, autorizációt és védelmet biztosít bizonyos támadási formák ellen. A Spring Security a de-facto szabványos eszköz a biztonság megvalósítására Springes alkalmazásokon belül.

A Spring Security támogatja a felhasználónév és jelszó párossal történő bejelentkezést, de ezen kívül pl. webszolgáltatások védelmére támogatja a HTTP BASIC, HTTP Digest és tanúsítvány alapú bejelentkezést, sőt az OAuth 2.0 használatát is.

A felhasználók és a hozzá kapcsolódó szerepkörök tárolhatóak memóriában, adatbázisban, LDAP szerveren, stb. Ezekhez adottak beépített implementációk, de saját is készíthető. Támogatja a jelszó hashelését különböző algoritmusokkal. A felhasználóval kapcsolatos információkat képes cache-elni is. Különböző eseményekre eseménykezelőket lehet aggatni, pl. bejelentkezés, így könnyen megoldható pl. audit naplózás.

Az alkalmazáson belül szerepkörökhöz lehet kötni bizonyos url-eket, valamint metódus szinten is meg lehet adni, hogy milyen szerepkörrel rendelkező felhasználó hívhatja meg.

A poszthoz egy példa projekt is tartozik, mely elérhető a GitHub-on. Egyszerű Spring Boot webes alkalmazás, Spring Data JPA perzisztens réteggel, Thymeleaf template engine-nel.

Modularizált alkalmazás fejlesztése a Spring Modulith-tal

Bevezetés

A microservice alkalmazások népszerűsége továbbra is töretlen. Miért is választják sokan ezt az architektúrát? Egyik ok természetesen a hype factor, sokan szeretnék kipróbálni, valamint hogy bekerüljön az önéletrajzukba. További ok, hogy sokan megcsömörlöttek a monolitikus alkalmazásoktól, hiszen sok kötöttséggel járnak mind fejlesztés, mind üzemeltetési oldalról. Ebből talán a legfontosabb, hogy gyakori jellemzője a spagetti kód, ennek következményeképp ha valahol belenyúlunk az alkalmazásba, lehet, hogy másik helyen romlik el, ezért ha telepíteni akarunk, ha biztosra akarunk menni, a teljes alkalmazást újra kéne tesztelnünk. Erre megoldás lehet a microservice architektúra, ahol az alkalmazásunkat lazán kapcsolódó szolgáltatásokra bontjuk fel. Ezzel kapcsolatban azonban a leggyakrabban elhangzó kérdés, hogy hol a határ, hol vágjunk, mi alapján bontsuk szét az alkalmazásunkat szolgáltatásokra. Monolitikus alkalmazásnál további kötöttségek a technológiai kötöttségek, valamint hogy a teljes alkalmazást egyben lehet csak telepíteni. És érdekes módon, csak ritkán szoktam azzal az indokkal találkozni, hogy azért választották a microservice architektúrát, mert gond volt a skálázhatósággal, aminek pedig nagy szerepe volt a kialakulásában.

Gyakran elfelejtjük, hogy egy monolitikus alkalmazásnak sem kéne szükségszerűen egyben lennie, hanem azt is felépíthetjük lazán kapcsolt komponensekből. A hiányzó láncszem itt a modul. A modularizált monolitikus alkalmazást szokás modulith-nak nevezni. Ennek létét két okból is fontosnak tartom. Egyrészt úgy vélem, hogy ahol nem tudnak modularizált alkalmazást fejleszteni, ott nem érdemes a microservice architektúrával foglalkozni, ugyanis ezek a technológiák nem mutatják meg, hogy hogy kell vágni. És a rossz vágásnak az eredménye ugyanúgy spagetti lesz, de már meg lesz nehezítve az elosztottságból adódó technológiai és üzemeltetési bonyodalmakkal is. Többek által is jónak tartott út a microservice-ek felé, hogy először modularizáljuk az alkalmazásunkat, majd utána emeljük ki a moduljainkat külön service-ekbe. A másik ok, amit érdemes észben tartani, hogy már egyre több helyről hallani, hogy a microservice architektúra nem vált be, nem váltotta be az ígéreteket, a szervezet még nem állt készen (pl. agilis módszertanok, DevOps, CI/CD hiánya - igen, ezek a microservice-ek előfeltételei), nem volt szükség skálázhatóságra, még rosszabb lett performanciában, stb.

Technológiailag a modulok azonban elég kevésbé támogatottak. Kezdeti próbálkozás volt az OSGi, azonban komplexitása miatt nem terjedt el, pedig olyan igéretei vannak, mint a futás közbeni plugin telepítés, valamint egy library-nek különböző verziói a classpath-on. Szabványos megoldást a Java Platform Module System próbált adni a Java 9-ben, de annak ellenére, hogy már mikor megjelent, szintén nem sikerült még elterjednie. A leggyakrabban használt megoldás a build rendszer által biztosított modularizáció, gondoljunk itt a Maven multi module projectre. Illetve a Gradle is azt hangoztatja, hogy multi module projektek kezelésében jobb és gyorsabb, mint a Maven. Azonban ez is plusz komplexitással jár, különösen a build folyamat, a CI/CD terén.

A kézenfekvő megoldás a Java csomagok használata lenne, azonban ez sajnos túl kevés eszközt ad a kezünkbe, a láthatósági módosítók csak nagyon szegényes hozzáférés szabályozást nyújtanak. Ennek kiegészítésére jelent meg az Spring Modulith projekt, mely több jó megoldást is ad. Nem hiszek feltétlenül abban, hogy ez az eszköz el fog terjedni, de a benne lévő ötleteket érdemes ismerni, és akár a saját projekjeinkben is bevezetni.

Perzisztens réteg technológiák és a MyBatis

Bevezetés

Amikor adatbázist kell választani, főleg a relációs adatbázisok és az SQL kerülnek szóba. Az ezzel való kapcsolattartásra az alkalmazás oldalon főleg a JPA szabványt, és annak valamelyik implementációját, pl. a Hibernate-et használjuk.

Régóta sokan ódzkodnak ettől a technológiától, különböző okok miatt. Hamar lehet vele eredményeket elérni, azonban nagyon nehéz megérteni a mélységeit. Ennek hiányában azonban az alkalmazásunk rosszul teljesíthet, az N + 1 probléma miatt nagyon sok SQL utasítást adhat ki, és mivel a JPA implementáció ezeket generálja, nem tudjuk finoman szabályozni. A JPA ezen kívül nehezebben alkalmazható egy már meglévő, esetleg nem körültekintően megtervezett adatbázisra, sokkal inkább támogatja azt, ha a Java osztályokból indulunk ki.

Ezért érdemes megnézni, hogy milyen alternatív technológiák vannak, és ezek milyen jellemzőkkel rendelkeznek.

A technológia kiválasztása valamilyen szinten hat az alkalmazásunk architektúrájára is. Többrétegű alkalmazás esetén a perzisztens réteg tartja a kapcsolatot az adatbázissal. Itt különböző architektúrális mintákat használhatunk, ebből egy pár darab:

  • Repository pattern, a Domain Driven Design (DDD) könyvből
  • Table és Row Data Gateway a Patterns of Enterprise Application Architecture (Martin Fowler) könyvből
  • Data Mapper ugyanonnan
  • Data Access Object (DAO) a Java EE tervezési minták közül

Bizonyos neveket ráadásul bizonyos technológiák is használnak, ilyen pl. a repository, melyet a Spring Framework is használ a perzisztens rétegének elnevezésére, amit a Spring Data JPA is átvett.

Az eredeti tervem az volt, hogy ezek jelentését részletesen kifejtem, és összehasonlítom őket. De rossz hírem van. Arra jöttem rá, hogy ezeket a fogalmak nincsenek jól definiálva, nem összehasonlíthatóak, és mindenki másra használja ezeket. És ezt a különböző technológiák tovább bonyolítják, ugyanis saját komponenseik elnevezésére használják ezeket a fogalmakat, helytelenül.

(Egy szemléletes példa erre, hogy a DDD szerint a repository egy olyan objektum, mely az üzleti logika és az és az üzleti objektumok adatbázisból olvasásáért vagy oda írásáért felelős ún. mapping réteg között helyezkedik el, és az üzleti objektumokat a kollekciókhoz hasonlóan kezeli. Ezen túl komplex lekérdezési lehetőséget is biztosít úgy, hogy a lekérdezési feltételeket dinamikusan lehet összeállítani. A Spring Data JPA repository-ja ezzel pont ellentétes, előre definiálnunk kell a metódusokat, melyek csak egy jól meghatározott feltétellel hívhatóak meg. Saját véleményem szerint a repository-nak a JPA Criteria Query API-ja sokkal jobban megfelel.)

Ezért úgy döntöttem, nem én fogok ezekben a fogalmakban rendet szabni, hanem inkább egy tulajdonság rendszert állítok össze, ami alapján a perzisztens technológiák osztályozhatóak. Ez legyen a következő:

  • Szabvány-e vagy egyedi implementáció
  • Ingyenes-e
  • SQL lekérdező nyelvet kell használni, vagy saját nyelve van, amiből maga generál le SQL utasításokat
  • Van-e benne mapping, azaz az adatbázisból jövő adatokat automatikusan meg tudja-e feleltetni az objektumokkal
  • Elég-e interfészeket definiálni, amihez maga generálja ki az implementációt.
  • Képes-e metódusnév alapján implementációt generálni

A mapping tipikusan reflectionnel működik, és rendelkezik alapkonfigurációval (pl. az attribútum neve megegyezik az oszlopnévvel), de ez személyre is szabható.

Azt is eldöntöttem, hogy nem a különböző tervezési minták neveit fogom használni, hanem a különböző technológiák elnevezési konvencióit.

Ezek alapján a JPA:

  • Szabványos, a Java EE szabvány része, implementációi pl. a Hibernate vagy az EclipseLink
  • Mindkét elterjedt implementációja ingyenes
  • Saját nyelve van, a JPQL, vagy a Criteria Query API is használható
  • (Object-Relational Mapping) ORM eszköz, azaz megfelelteti az objektumokat az adatbázisból jövő adatokkal
  • Nem elég interfészeket definiálni
  • Ezért metódusnév alapján sem tud implementációt generálni

Mi várható a Spring Boot 3-ban?

Bevezetés

Mivel a hétvégén megkaptam, hogy írjak már Javas cikkeket, így ebben a posztban a Spring Boot 3 újdonságait veszem sorra. A Spring Boot 3-as sorozat már a Spring Framework 6-os sorozatára építkezik, ennek újdonságait nem fogom külön tárgyalni. A poszt megírásának a pillanatában a legfrissebb verzió a 3.0.0-M4, és mivel erőteljesen fejlesztés alatt van, még változhat, érdemes visszanézni, fogom majd frissíteni a posztot.

Általános megfigyelésem, hogy az új fejlesztéseket még nem dokumentálták megfelelően, így sokmindent a Release Notesokból, GitHub issue-kból és a forráskódból kellett összeszedni.

Az említendő változások a következő területeket érintik:

  • Alapkövetelmény a Java 17
  • Jakarta EE 9 függőségek
  • Problem Details
  • Tracing
  • Natív futtatható fájl elkészítése

Fejlesztőként mivel akadályozom a tesztelők munkáját?

Bevezetés

Frissítve: 2022. november 04-én, kiegészítve a Rossz gyakorlat: Egy projekten már teszteltünk, nem vált be, a Rossz gyakorlat: nem megfelelő üzleti fogalmakat használok, a Rossz gyakorlat: nem megfelelő branch-en történik a tesztelés, és a Rossz gyakorlat: nem bontom részproblémákra a problémát fejezetekkel

Frissítve: 2022. augusztus 21-én, kiegészítve a Naplózás fontossága résszel, valamint a cache-re vonatkozó ajánlásokkal

Ahhoz, hogy sikeres szoftvert tudjunk szállítani, hiszem, hogy nagyon fontos a fejlesztők és a tesztelők közötti szoros együttműködés. És mikor tesztelőket említek, ugyanúgy gondolok a manuális és automata tesztelőkre is. Az irányzat, mely a fejlesztők és az üzemeltetők közötti kapcsolat fontosságát hangsúlyozza, a DevOps nevet kapta. Célja egy olyan kultúra kialakítása, gyakorlatok és eszközök kiválasztása, ahol a fejlesztők és az üzemeltetők közös munkával tudnak gyorsan, megbízhatóan alkalmazásokat és megoldásokat szállítani. Ez a fogalom manapság igencsak felkapott, de méltatlannak érzem, hogy a tesztelőkkel való közös munka fontosságának kiemelése korántsem ennyire hangsúlyos.

Történetileg kialakult, hogy a fejlesztés és az üzemeltetés a legtöbb cégnél elvált egymástól, tisztán elválasztott szervezeti egységekben, sőt akár külön cégekben működtek, melyek között a kommunikáció finoman szólva is döcögős volt. Sőt, úgy érződött, hogy a két csoportnak eltérő a célja. Az üzemeltetők stabilitást, biztonságot, tervezhetőséget szerettek volna, míg a fejlesztők mindig az örökös változásért, fejlődésért harcoltak. Észrevehetjük, ugyanez megfigyelhető a tesztelőknél is, egyrészt a különválás, a nehézkes kommunikáció, és a látszólag ellentétes cél. Van, hogy a fejlesztők által késznek minősített alkalmazást a tesztelők “visszadobnak”. Kezdjük felismerni, hogy az üzemeltetés és a fejlesztés szétválasztása káros, de vajon így érezzük a tesztelők munkájával kapcsolatban is? Ráadásul az egyes agilis módszertanok, mint pl. a Scrum szerint a csapat felelős a sprint végén a kész termék leszállításáért, és ebben olyan egyenrangú csapattagok vesznek részt, akik persze rendelkeznek speciális ismeretekkel, pl. üzleti elemzés, fejlesztés, tesztelés vagy üzemeltetés.

Voltam olyan helyen, ahol a fentebb bemutatott elszigeteltség jelen volt, és mindig dolgoztam ennek csökkentésén. Voltan olyan projekten is, ahol ezen különböző ismeretekkel rendelkező szakemberek egy csapatban dolgoztak. Régóta tartok fejlesztői tanfolyamokat, köztük kezdő fejlesztői tanfolyamokat, amin egyre több tesztelővel találkozok, aki el akar mozdulni az automata tesztelés irányába. Sőt részt vettem tesztelői bootcampek megszervezésében is, ahol nagyon sokat tanultam a tesztelő kollégáimtól, és beleláthattam ennek a szakmának a szépségeibe is, és erősítették bennem a hitet, hogy mennyire fontos az együttműködés. Sőt, automata teszt eszközökkel kapcsolatos képzéseket is tartok, amin szintén sok tesztelő vesz részt.

Ezen tapasztalataim alapján, és a tesztelők (akár szünetekben elmesélt) történeteit meghallgatva gyakran úgy látom, hogy a fejlesztőknek a teszteléssel kapcsolatban rengeteg tévhit él a fejében, és rengeteg rossz gyakorlatot folytatnak. Ebben a posztban ezeket próbálom felsorolni, megcáfolni. Lesz szó általános elvekről, de pár helyen lemegyek technológiai szintre is. Egyes szám első személyben fogok írni, fejlesztő lévén, még akkor is, ha magam nem így gondolkozom, így megpróbálok senkit sem megsérteni. Félreértés ne essék, nem ellentéteket szeretnék szítani, hanem megoldásokat kínálni.