Security

A Biztonság rengeteg mindent jelenthet az IT világban is. Mivel rengeteg kontextusban előjöhet a biztonság kérdése, így első körben tisztáznunk kell, hogy hol lehet azt alkalmazni. Ugyanakkor legyen szó bármilyen kontextusról, végül mindig egy a célunk: az érzékeny adatok és az értékes erőforrások védelme.

Mivel egy alkalmazás manapság rengeteg rétegből állhat (ideértve az infrastrukturális rétegeket is), minden rétegben gondoskodnunk kell a megfelelő védelemről.

A védelem első lépcsője a védelem mélységének megtervezése (Defense in Depth - DiD), mely a következőkből áll:

  • Adminisztratív kontroll: policy-k, eljárás, útmutatások
  • Fizikai kontroll: szerver szobák biztonsága, videókamerák alkalmazása
  • Technikai kontroll: Tűzfal, antivírus, IPS (intrusion prevention systems)

Ez utóbbi több rétegből tevődik össze (Perimeter, Network, Endpoint, Application, Data/Information).

Hálózati réteg: Tipikusan a tűzfalakkal mosódik össze a fogalom. A legfontosabb ebben a rétegben számunkra az SSL (Secure Sockets Layer) alkalmazása, mely az összes küldött adatot titkosított módon továbbítja.

Operációs rendszer réteg: Az egyik legfontosabb összetevő maga az operációs rendszer védelme, ami az adott szerveren fut (melyen majd az alkalmazásunkat is futtatni szeretnénk).

Alkalmazás réteg: Ez az ahol, mi fejlesztők a legtöbbet tudjuk tenni annak érdekében, hogy az alkalmazásunk megfelelően biztosítva legyen a kártékony használattól. Az alkalmazásunk védelme érdekében a Spring által biztosított technikákat mutatjuk be.

Alapfogalmak

  • Autentikáció: lényege, hogy egy adott felhasználóról eldöntsük, hogy valóban az akinek mondja magát. A folyamat során a felhasználó olyan adatot szolgáltat magáról (általában felhasználónév és jelszó), melyet jobb esetben senki más nem tud. Ezután a rendszerben összevetjük a megadott adatokat a tárolt adatokkal (Pl.: Adatbázis vagy LDAP). Sikeres összehasonlítása esetén a felhasználó sikeresen autentikálta magát.
  • Autorizáció: Autentikáció esetén annyit mondtunk, hogy felismertük a felhasználót, de ez nem azt jelenti, hogy bármit tehet a rendszeren belül. A következő lépcsőfok, hogy meg kell vizsgálnunk a jogosultságait, azaz milyen adatokon milyen műveleteket végezhet.
  • ACL (Access Control List): Az autorizációs folyamat eleme. Segítségével finomabb felbontású jogosultságkezelés valósítható meg. Igazából egy leképezés megadása az erőforrások, felhasználók és jogosultságok között.
  • User: A legitim felhasználóknak hozzáférést kell biztosítanunk, melyeket a rendszeren belül valamilyen absztrakciós objektumoknak megfeleltetünk (alkalmazás belüli User objektumok).
  • Credentials: amivel a felhasználó azonosítani tudja magát (általában egy jelszó, vagy valamilyen tanúsítvány). A lényeg, hogy csak a felhasználó legyen birtokában.
  • Role: A felhasználók logikai csoportosítása. Általában könnyebb a jogosultságokat csoportokra megadni, mint a felhasználókra egyesével.
  • Resource: Biztonság tekintetében olyan erőforrásokra gondolunk, melyek hozzáférését megfelelően korlátozni kell.
  • Permission: Az a hozzáférési szint, mely minimálisan szükséges, hogy egy adott erőforrást használni tudjunk.
  • Encryption: Érzékeny adatok titkosítása (például a jelszót soha ne tároljuk plain textként). Titkosítási algoritmus típusok:
    • One-way encryption: másnéven a hash-alapú algoritmusok, melyek egy string inputból egy szám output-ot gyártanak melyet szokás message digest-nek hívni. Az output nem visszafejthető, azaz nem tudjuk belőle visszanyerni az eredeti input-ot (ezért egyirányú). Használat során a szerver a megkapott (jobb esetben titkosított) adatra ugyanazt a hash-elési eljárást alkalmazza, melyet az előző esetben is használt (regisztrációkor), majd a két hash-elt értéket összehasonlítja és egyezést vizsgál. Tehát a hash-elt értékek alapján tudjuk csak összehasonlítani az értékeket.
    • Symmetric encryption: encrypt és decrypt párost is adnak ezek a módszerek, azaz a titkosított sztring visszaalakítható az eredeti formába. A küldő és a fogadó általában rendelkezik ugyanazzal a kulccsal, mellyel az üzenetet kódolni és dekódolni is lehet. Ennek a titkosításnak nagy kérdése, hogy hogyan osszuk meg csak a jogosultakkal a szükséges kulcsot? Általában ezt egy ún. parallel secure channel-el teszik meg.
    • Public key cryptography: aszimmetrikus titkosításon alapszik, azaz egy kulccsal titkosíthatunk és egy másikkal kaphatjuk vissza az eredeti üzenetet. A titkosításhoz használatos kulcs a publikus kulcs, a dekódoláshoz használt kulcs pedig a privát kulcs. Az előnye, hogy a dekódoláshoz használt privát kulcsot nem kell megosztani, azt csak a fogadónak kell ismernie. A folyamatban a jövőbeli fogadó megosztja a publikus kulcsot a küldővel, így ő tudja az üzenetét kódolni. Ezután elküldi az üzenetet, melyet a privát kulccsal dekódolni tud a fogadó.
  • Confidentiality: A potenciálisan szenzitív adat, mely valamely felhasználó tulajdonát képezi, csak az adott felhasználó által érhető el (vagy akinek a felhasználó jogot biztosít hozzá). Ebben a titkosítás van segítségünkre.
  • Integrity: A felhasználó által küldött adatokat nem módosíthatja egy third-party sem, míg az adatok elérkeznek a szerverhez. Általában egyirányú titkosítással orvosolható.

Leggyakoribb támadási formák

  • SQL injection: SQL vagy szerver oldali kód megadása felhasználói inputként, mellyel a szerver erőforrásait akár meg is szerezheti a támadó.
  • Denial of Service (DoS): A támadás célpontjának megbénítása, melyet általában rengeteg célzott kéréssel visznek véghez. Eredményeképpen a rendszer legitim használói sem tudják megfelelően használni a rendszert.
  • Cross-site Scripting (XSS): Az alkalmazás kliens oldala jelenti a célpontot, melynek során a válaszul adott weblapba a kártékony elemeket helyez el a támadó, melyek futtatása a felhasználó böngészőjében történik.

Alap Java támogatások

A Java és a JavaEE több beépített megoldást is kínál a biztonság elérése érdekében, itt csak nagy vonalakban felsoroljuk ezen API-kat:

  • Java Cryptography Architecture (JCA): kriptográfiai algoritmusok, melyek között megtalálható a hash-digest és a digitális aláírás támogatás.
  • Java Cryptographic Extensions (JCE): sztringek enkriptálásához és dekriptálásához használható algoritmusok, titkosított kulcs generálás szimmetrikus titkosításhoz.
  • Java Certification Path API (CertPath): Tanúsítványok validációja és verifikációja.
  • Java Secure Socket Extension (JSSE): SSL és TLS protokoll támogatás mind kliens és szerver oldalon.
  • Java Authentication and Authorization Service (JAAS): Autentikáció és autorizáció támogatása, továbbá plugin-elhető.

A Spring adta lehetőségek leginkább a JAAS-hoz kapcsolódnak, fedik azt el, bár a kettőt együttesen is alkalmazhatjuk. A többi API-hoz általában valamilyen pehelysúlyú megvalósítás tartozik Springben. Például a CertPath Springen belül az X509AuthenticationFilter formájában jelentkezik, a JCE megfeleltethető a spring-security-crypto moduljának.

Spring Security architektúra

Ha legmesszebbről nézzük a Spring Security architektúrát, akkor az nem más, mint interceptorok alkalmazása, melyek eldöntik, hogy az adott erőforráshoz van-e jogosultsága a felhasználónak vagy sem.

Interceptor: Webes környezetben az interceptorok beékelődnek a kérés és a kiszolgálás közé, melyben extra lépéseket adhatunk meg. Alapvetően egy tervezési minta, melynek használati előnye, hogy nem kell semmit sem módosítanunk a már megírt logikán. Az egyik egyszerű példa az interceptorok használatára a javax.servlet.Filter.

Ha közelebbről nézzük a Spring Security-t, akkor meg is érkezünk a filterek és az AOP világába.

Aspektus orientált programozás (AOP)

Itt nem célunk az AOP bemutatása, viszont az érdemes tudni, hogy segítségével gyakran ismétlődő kódrészeket kiszervezhetünk egy úgynevezett aspektusba (egy külön osztályba), mely segítségével a rendszer más pontjainak viselkedését (metódusait) becsomagolhatjuk és a tényleges üzleti logika előtt és után is végezhetünk műveleteket. A nagy előnye, hogy így egy helyen kell karbantartani a kódot és a rendszer tetszőleg pontján alkalmazni lehet a megírt aspektusainkat. Segítségével csökkenthetjük az alkalmazásunkban az egzakt megadott csatolásait is, melyről már korábban is láttuk, hogy nagy előnyt jelent. Tipikus alkalmazásai a loggolás, tranzakciókezelés és nem meglepő módon a biztonság.

Az interceptorok két elem köré összpontosulnak: az URL-ek és az üzleti logikát tartalmazó metódushívások köré. Ezeket az ún. entry point-okat a Spring becsomagolja és csak akkor engedélyezi az azokhoz történő hozzáférést, ha a biztonsági előírásokat maradéktalanul teljesítjük. Ebben a folyamatban a főkolompos a Security Interceptor, mely végső soron meghozza a döntést. Az ehhez tartozó fő interface az AbstractSecurityInterceptor, mely rendelkezik két implementációval: FilterSecurityInterceptor és MethodSecurityInterceptor.

Ahogy az interceptorok többsége működni szokott, itt is van egy preprocesszálási fázis és egy postprocesszálási fázis, melyek között a tényleges logika fut. A preprocesszálási részben megtörténi annak ellenőrzése, hogy a kért erőforrás védett-e. Amennyiben nem, akkor továbbengedjük a kérést az adott URL-re vagy metódusra. Fordított esetben viszont a SecurityInterceptor lekéri az Authentication objektumot az aktuális SecurityContext-től. Amennyiben szükséges magát az autentikációt is elvégezni (például még nem jelentkezett be a felhasználó), akkor a SecurityInerceptor által ismert AuthenticationManager-rel elvégezteti az autentikációt.

Az AuthenticationManager alapvetően 3 dolgot tehet:

  • Visszaad egy Authentication objektumot, melyben az authenticated = true, ha a megadott input alapján megerősíthetjük a felhasználó kilétét (helyes username és password).
  • dob egy AuthenticationException kivételt, ha a megadott adatok helytelenek
  • Ha nem tudja eldönteni, akkor null-t ad vissza.

Miután az autentikációs folyamat sikeresen lezajlott, az Authentication (mely tartalmazza a jogosultságokat is) objektum, a kért erőforrás és az erőforrásra megadott hozzáférési metaadatok (ConfigAttribute objektumok) alapján az úgynevezett AccessDecisionManager dönti el, hogy hozzáférhetünk-e a kért erőforráshoz. Amennyiben a megadott információk alapján nem vagyunk jogosultak a hozzáférésre, akkor egy AccessDeniedException keletkezik.

Miután a fenti folyamat lezajlott akkor a Security Interceptor létrehoz egy InterceptorStatusToken-t (security context-et és metaadatokat is tartalmazza). Ez a visszaadott token a postprocessing lépésben használható fel.

Ezen a ponton jutottunk el odáig, hogy a kérést továbbengedjük a kért erőforráshoz. Amint a kérést kiszolgáljuk és elindulunk "visszafelé", akkor fut le a postprocessing fázis, mely már sokkal kevesebb történést tartalmaz. Itt egy opcionálisan használható AfterInvocationManager filterezheti a kéréseket, de webes környezetben ezt nem szoktuk használni.

Biztonság Servlet alapú alkalmazásokban

A Spring is a filter láncokat (filter chain) használja a biztonság biztosításához (többek között). Ez a filter chain modell a standart servlet filter chain-re alapszik, mely egyfajta interceptor tervezési mintának is tekinthető. A filter chain elemei rendre jól definiált filterek, melyek csak egy-egy biztonsági elemet vizsgálnak, majd a láncban következő filternek adják át a kérést. Amikor beérkezik egy kérés akkor a konténer ad hozzá egy filter chain-t is, melyekben benne vannak a filterek és melyek mindegyike a HttpServletRequest-et dolgozhatja fel. Fontos, hogy egy kérést csak egy szervlet dolgozhat fel, viszont bármennyi filtert megadhatunk a láncban. A filterek akár meg is akadályozhatják, hogy a kérés eljusson a servletig, mely esetben maga a filter állítja elő a választ. A filterek szabadon módosíthatják a HttpServletRequest és HttpServletResponse objektumokat.

filtering

A filterek ereje magából a filterchain-ből jön. Tekintsünk egy tradicionális filter megadást:

1
2
3
4
5
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) {
    // do something before the rest of the application
    chain.doFilter(request, response); // invoke the rest of the application
    // do something after the rest of the application
}

Mivel egy filter szabadon módosíthatja a kérés és válasz objektumokat, így rettentően fontos a filterek megadásának sorrendje.

DelegatingFilterProxy

A Spring minden egyes filtere egy-egy Spring bean, de erről a hagyományos servlet alapú filter chain mit sem tud. Szükség van viszont egy olyan filter-re, amely áthidalja a hézagot a servlet konténer életciklus kezelése és a Spring-es ApplicationContext között, hogy a Spring-es bean alapú filtereket működésre bírjuk. Ez a filter implementáció a DelegatingFilterProxy, melyet a hagyományos filter láncban megadhatunk, de ő maga csak továbbítja a kéréseket a bean alapú filtereknek (melyek a Filter-t implementálják).

filter proxy

A DelegatingFilterProxy megkeresi a Bean Filter-t az ApplicationContext-ben, majd ezután meghívja annak filter metódusát.

FilterChainProxy

A DelegatingFilterProxy belül egy FilterChainProxy-t használ arra, mely ténylegesen képes arra, hogy a bean alapú filterek láncát hívja meg. A fenti DelegatingFilterProxy ténylegesen csak az áthidaló szerepet tölti be. A FilterChainProxy kezeli a SecurityFilterChain-t, melyben a Bean alapú filterek találhatóak meg.

filter proxy

A fenti architektúra előnye, hogy így biztosítani tudjuk a biztonsággal kapcsolatos filterezések belépési pontját (FilterProxyChain). Mivel a FilterProxyChain egy központi elem, így itt elvégezhetjük a kötelező, minden kérésre szükséges eljárásokat. Például a SecurityContext ürítése minden kérés után (memory leak elkerülése végett), illetve egy HttpFirewall is müködik belül, ami védelmet nyújt néhány támadás ellen. Ezen felül sokkal rugalmasabban kezelhetjük a filterek futását, hiszen míg a tradicionális kontextusban URL alapon filterezhetünk, addig itt bármi alapján ami a HttpServletRequest-ben benne van.

Még egy lépcsővokkal feljebb lépve azt láthatjuk, hogy a FilterProxyChain több SecurityFilterChain-t kezelhet egyszerre, így dinamikusan dönthet arról, hogy, melyik chain-t kell alkalmazni (például egyes url-ekre teljesen más filterezés fut le mint egy másikra). Ebben az esetben viszont fontos, hogy csak egy SecurityFilterChain-t választhat ki a rendszer (a sorban a legelsőt, ami megfelel a kritériumoknak).

Kivételek kezelése

Amikor a Spring Security-t használjuk a biztonsági ellenőrzések során kétféle fő kivétel dobódhat:

  • AccessDeniedException
  • AuthenticationException

melyeket az ún. ExceptionTranslationFilter kezeli, mely maga is a filter chain része lesz. Ez a filter hidat képez a Java-s kivételek és a HTTP válaszok között, azaz a fenti exception-öket átalakítja a megfelelő HTTP válaszokra.

exception_translation

Első lépésként az ExceptionTranslationFilter meghívja a FilterChain.doFilter(request, response)-t, mellyel továbbdobja a kérés és válasz objektumokat a láncban a következő filternek. Ha a felhasználó nincs autentikálva, akkor keletkezik egy AuthenticationException, majd megkezdődik a felhasználó bejelentkeztetése. Ilyenkor a SecurityContextHolder-t kiüríti a rendszer, a beérkezett kérést elmenti a RequestCache-be (bejelentkezés után az eredeti kérést végre tudjuk hajtani, nem kell a felhasználónak újra kiadnia a kérését), ezután pedig a felhasználótól kérjük a credentials (alapesetben a jelszót), melyet az AuthenticationEntryPoint határoz meg (pl.: átirányít a login oldalra).

Amennyiben AccessDenied kivétel keletkezik, akkor az erre a célra számontartott AccessDeniedHandler intézi a továbbiakat.

Ha menet közben nem kapunk semmilyen kivételt, akkor az ExceptionTranslationFilter nem csinál semmit. Az ExceptionTranslationFilter pseudo kódja valami ilyesmi:

1
2
3
4
5
6
7
8
9
try {
    filterChain.doFilter(request, response); 
} catch (AccessDeniedException | AuthenticationException ex) {
    if (!authenticated || ex instanceof AuthenticationException) {
        startAuthentication(); 
    } else {
        accessDenied(); 
    }
}

Ez azt jelenti, hogy ha az ExceptionTranslationFilter utáni bármelyik filter a fenti két kivétel közül bármelyiket dobja akkor az ExceptionTranslationFilter azok ezen a ponton lesznek kezelve.

Autentikáció

Kezdjük az architekturális komponensekkel:

  • SecurityContextHolder: A Spring itt tárolja azon információt, hogy ki az aki be van jelentkezve
  • SecurityContext: A SecurityContextHolder-től elkérhető kontextus, mely tartalmazza az Authentication objektumot, mely az aktuálisan bejelentkezett felhasználó adatait adja meg.
  • Authentication: Az AuthenticationManager egy lehetséges bemenete lehet, melyben a felhasználó megadta a credentials-t (általában felhasználónév és jelszó), mely alapján a eldönti az AuthenticationManager, hogy beléphet-e a felhasználó. Amennyiben már bejelentkezett a felhasználó, akkor az Authentication objektum az aktuálisan bejelentkezett felhasználó adatait tárolja.
  • GrantedAuthority: A jogosultság, melyet a felhasználó megkapott e bejelntkezés során (pl.: role, scope)
  • AuthenticationManager: Az az API, amely megmondja, hogy a Spring-es Security Filterek, hogyan autentikálnak
  • ProviderManager: Az AuthenticationManager interfész egyik leggyakrabban használt konkrét megvalósítása
  • AuthenticationProvider: a ProviderManager használja, hogy konkrét autentikációs típust alkalmazzon
  • Request Credentials with AuthenticationEntryPoint: A klienstől való credentials kérésére használandó (pl.: login oldalra való átirányítás vagy WWW-Authenticate header küldése). Az AuthenticationEntryPoint használható arra, hogy egy HTTP kérést küldjünk a felhasználó felé, melyben az azonosításhoz szükséges információkat kérjük el (credentials).
  • AbstractAuthenticationProcessingFilter: Egy alap autentikációs filter, mely felépítése jól mutatja az egyes komponensek egymáshoz való viszonyát

Az alább néhányat láthatunk a Spring autentikációs mechanizmusai közül:

  • Username and Password
  • OAuth 2.0 Login
  • SAML 2.0 Login
  • Central Authentication Server (CAS)
  • Remember Me
  • JAAS Authentication
  • OpenID
  • Pre-Authentication Scenarios
  • X509 Authentication - X509 Authentication

Ezek után vizsgáljuk meg a fenti komponenseket alaposabban is!

SecurityContextHolder

Az autentikációs modell szíve, mely tartalmazza a SecurityContext-et.

security_context_holder

A Spring nem törődik, hogy a SecurityContextHolder-be hogyan kerülnek bele a felhasználónak az adatai, ha azok ott vannak, akkor a user-t autentikáltnak tekinti. Akár direktben is elhelyezhetünk oda információt:

1
2
3
4
5
6
SecurityContext context = SecurityContextHolder.createEmptyContext(); 
Authentication authentication =
    new TestingAuthenticationToken("username", "password", "ROLE_USER"); 
context.setAuthentication(authentication);

SecurityContextHolder.setContext(context);

Ebben az esetben fontos, hogy új kontextet csináljunk és ne a meglévőt használjuk, mert akkor a szálon belül elronthatjuk a dolgokat. A fenti TestingAuthenticationToken ne használjuk prod kódban, helyette inkább a UsernamePasswordAuthenticationToken(userDetails, password, authorities) használatos.

Egy gyakori scenario lehet, hogy az aktuális felhasználó adatait lekérjük. Ez az alábbi módon tehető meg:

1
2
3
4
5
SecurityContext context = SecurityContextHolder.getContext();
Authentication authentication = context.getAuthentication();
String username = authentication.getName();
Object principal = authentication.getPrincipal();
Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();

Alapértelmezetten a SecurityContextHolder a ThreadLocal-t használja, mely eredményeképpen a fenti információk bárhonnan elérhetőek a szálon belülről. A SecurityContext létezéséről a FilterChainProxy gondoskodik.

Authentication objektum

Mint ahogy azt már említettük kettős szerepet tölt be az objektum:

  • Egyrészt az AuthenticationManager számára már egy Authentication objektumot adunk át, melyben a felhasználó nevét és jelszavát átadjuk. Ilyenkor az isAuthenticated() metódusa még hamis értéket ad vissza.
  • Másrészt az aktuálisan bejelentkezett felhasználó adatait tárolhatja egy Authentication objektum, mely ilyen esetben már autentikált.

Az bjektum tartalma:

  • principal: Tetszőleges objektum, mely azonosítja a felhasználót. Felhasználónév és jelszó esetében általában egy UserDetails típusú objektum.
  • credentials: A felhasználó jelszavát tárolja, azonban ezt általában eldobja a rendszer a sikeres autentikáció után, hogy nehogy kiszivárogjon
  • authorities: azok a GrantedAuthority objektumok, melyek magas szintű jogosultságokat tesznek lehetővé. Például szerepkörök. Ezeket aztán később az autorizáció során felhasználhatjuk. Amikor felhasználónév és jelszó párossal operálunk, akkor ezeket a jogokat az ún. UserDetailsService tölti be.

ProviderManager

A leggyakrabban használt AuthenticationManager implementáció. A ProviderManager a beérkező autentikációs kéréseket több AuthenticationProvider-nek teríti/terítheti széjjel., attl függően, hogy milyen autentikációra van szükség. Minden AuthenticationProvider saját maga jelezheti, hogy sikeres, sikertelen az autentikáció vagy esetleg nem tudja eldönteni. Amennyiben egyik AuthenticationProvider sem jelzett vissza sikeres autentikáció, akkor egy AuthenticationException kivétel keletkezik.

Példa konkrét AuthenticationProvider-re:

  • DaoAuthenticationProvider: felhasználónév/jelszó alapú autentikáció
  • JwtAuthenticationProvider: token alapú autentikáció

Megjegyzés

Egy ProviderManager rendelkezhet egy parent-el, aki segít neki dönteni olyan esetekben amikor ő nem tud. Sőt mi több egy ilyen parent-nek lehet több gyereke is, mely akkor jöhet jól ha különböző autentikációs mechanizmusokat alkalmazunk, de van közös rész is az autentikációban.

Username/Password Authentication

A leggyakoribb autentikációs módszer, melyhez a Spring ad is néhány beépített autentikációs mechanizmust, melyekkel a HttpServletRequest-ből kiolvashatjuk a felhasználónevet és a jelszót:

  • Form Login
  • Basic Authentication
  • Digest Authentication

A fenti mechanizmusok mindegyikéhez megadható, hogy honnan veszi az érvényesnek tekintett adatokat, amellyel összeveti a felhasználó által megadott adatokat. Ezek a következőek lehetnek:

  • Simple Storage In-Memory autentikációval
  • Relációs Adatbázis JDBC autentikációval
  • Egyedi adattároló UserDetailsService használatával
  • LDAP tároló LDAP autentikációval

Form Login

Az alábbi ábra azt mutatja be, hogy miként kerül a felhasználó a login oldalra.

login-form

  1. Első lépésben küld egy kérést egy védett erőforrásra (ilyenkor még nincs bejelentkezve).
  2. A FilterSecurityInterceptor dob egy AuthenticationExeption-t, mivel még nincs bejelentkezve
  3. Akcióba lép az ExceptionTranslationFilter és egy autentikációs folyamatot indítványoz, mely alapján küldd a kliensnek egy redirect választ, melyben az AuthenticationEntryPoint (általában egy LoginUrlAuthenticationEntryPoint objektum) által konfigurált oldalra viszi a klienst.
  4. A böngésző lekéri a login oldalt
  5. Login oldal renderelése

Amikor a felhasználó megadja a felhasználónevét és jelszavát, akkor az UsernamePasswordAuthenticationFilter autentikálja a felhasználót.

A form login alapból engedélyezve van a Springben. A form login megadása a biztonsági konfigurációknál (extends WebSecurityConfigurerAdapter)

1
2
3
4
5
protected void configure(HttpSecurity http) {
    http
        // ...
        .formLogin(withDefaults());
}

Ebben az esetben az alapértelmezett login oldalt fogjuk renderelni, melyet a Spring biztosít. Amennyiben nem az alapértelmezett login oldal-t szeretnénk használni, akkor a következőképpen kell eljárnunk:

1
2
3
4
5
6
7
8
protected void configure(HttpSecurity http) throws Exception {
    http
        // ...
        .formLogin(form -> form
            .loginPage("/login")
            .permitAll()
        );
}

Basic Authentication

login-form

Ebben az autentikácis eljárásban a következők történnek:

  1. Első lépésben küld egy kérést egy védett erőforrásra (ilyenkor még nincs bejelentkezve).
  2. A FilterSecurityInterceptor dob egy AuthenticationExeption-t, mivel még nincs bejelentkezve
  3. Akcióba lép az ExceptionTranslationFilter és egy autentikációs folyamatot indítványoz. A login formhoz képest az a különbség, hogy itt az AuthenticationEntryPoint egy BasicAuthenticationEntryPoint objektum, ami egy WWW-Authenticate header-ű választ küldd vissza a kliensnek.

In-memory Authentication

Az InMemoryUserDetailsManager implementálja a UserDetailsService-t, hogy felhasználónév és jelszó alapú autentikációt biztosítson, melynek az autentikációs információit a memóriában tárolja. Ilyen módon lehetséges a UserDetails használata is:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
@Bean
public UserDetailsService users() {
    UserDetails user = User.builder()
        .username("user")
        .password("{bcrypt}$2a$10$GRLdNijSQMUvl/au9ofL.eDwmoohzzS7.rmNSJZ.0FxO/BTk76klW")
        .roles("USER")
        .build();
    UserDetails admin = User.builder()
        .username("admin")
        .password("{bcrypt}$2a$10$GRLdNijSQMUvl/au9ofL.eDwmoohzzS7.rmNSJZ.0FxO/BTk76klW")
        .roles("USER", "ADMIN")
        .build();
    return new InMemoryUserDetailsManager(user, admin);
}

JDBC Authentication

TODO

Autorizáció

TODO


Utolsó frissítés: 2020-10-20 13:45:04