Extra bejelentkezési mezők tavaszi biztonsággal

1. Bemutatkozás

Ebben a cikkben egy egyedi hitelesítési forgatókönyvet valósítunk meg a Spring Security által egy extra mező hozzáadása a szokásos bejelentkezési űrlaphoz.

Arra fogunk koncentrálni 2 különböző megközelítés, hogy megmutassa a keret sokoldalúságát és a rugalmas felhasználási módokat.

Első megközelítésünk egyszerű megoldás lesz, amely a meglévő tavaszi biztonsági alapelvek újrafelhasználására összpontosít.

A második megközelítésünk testre szabottabb megoldás lesz, amely alkalmasabb lehet a haladóbb esetekre.

A tavaszi biztonsági bejelentkezésről szóló korábbi cikkekben tárgyalt koncepciókra építünk.

2. Maven Setup

A Spring Boot indítókat használjuk a projektünk indításához és az összes szükséges függőség behozatalához.

Az általunk használt beállításhoz szülői nyilatkozat, webindító és biztonsági indító szükséges; felvesszük a tűlevelűeket is:

 org.springframework.boot spring-boot-starter-parent 2.2.6.RELEASE org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-security org.springframework.boot spring-boot- starter-thymeleaf org.thymeleaf.extras thymeleaf-extras-springsecurity5 

A Spring Boot biztonsági indító legújabb verziója megtalálható a Maven Central oldalán.

3. Egyszerű projektbeállítás

Első megközelítésünkben a Spring Security által biztosított megvalósítások újrafelhasználására összpontosítunk. Különösen újrafelhasználjuk DaoAuthenticationProvider és FelhasználónévPasswordToken mivel „dobozon kívül” léteznek.

A legfontosabb elemek a következők lesznek:

  • SimpleAuthenticationFilterkiterjesztése FelhasználónévPasswordAuthenticationFilter
  • SimpleUserDetailsServicemegvalósítása UserDetailsService
  • Minketera. kiterjesztése Felhasználó osztály, amelyet a Spring Security biztosít, amely kijelenti az extránkat tartomány terület
  • SecurityConfigtavaszi biztonsági konfigurációnk, amely beilleszti a SimpleAuthenticationFilter be a szűrő láncba, deklarálja a biztonsági szabályokat és összekapcsolja a függőségeket
  • login.htmlegy bejelentkezési oldal, amely összegyűjti a felhasználónév, Jelszó, és tartomány

3.1. Egyszerű hitelesítési szűrő

Miénkben SimpleAuthenticationFilter, a domain és a felhasználónév mezőket kivonják a kérésből. Összekapcsoljuk ezeket az értékeket, és felhasználjuk egy példány létrehozására FelhasználónévPasswordAuthenticationToken.

A tokent ezután továbbítják a AuthenticationProvider hitelesítéshez:

public class SimpleAuthenticationFilter kiterjeszti a UsernamePasswordAuthenticationFilter {@Override nyilvános hitelesítési kísérletAuthentication (HttpServletRequest kérés, HttpServletResponse válasz) dobja az AuthenticationException {// ... UsernamePasswordAuthenticationToken authRequest = getAuthRequ setDetails (kérés, authRequest); adja vissza ezt :getAuthenticationManager () .authenticate (authRequest); } privát felhasználónévPasswordAuthenticationToken getAuthRequest (HttpServletRequest kérés) {String felhasználónév = getUsername (kérelem); String jelszó = getPassword (kérés); Karakterlánc tartomány = getDomain (kérés); // ... String felhasználónévDomain = String.format ("% s% s% s", felhasználónév.trim (), String.valueOf (Karakter.LINE_SEPARATOR), tartomány); return new FelhasználónévPasswordAuthenticationToken (felhasználónévDomain, jelszó); } // egyéb módszerek}

3.2. Egyszerű UserDetails Szolgáltatás

A UserDetailsService a szerződés egyetlen módszert határoz meg loadUserByUsername. Megvalósításunk kivonja a felhasználónév és tartomány. Az értékeket ezután átadjuk az U-nakserRepository hogy megkapja a Felhasználó:

public class SimpleUserDetailsService megvalósítja a UserDetailsService {// ... @A nyilvános UserDetails felülbírálása loadUserByUsername (karakterlánc felhasználónév) dob felhasználónév nemFoundException {karakterlánc [] felhasználónévAndDomain = StringUtils.split (felhasználónév, String.valueOf) (Character.LINE); if (felhasználónévAndDomain == null || felhasználónévAndDomain.length! = 2) {dobja új felhasználónévNotFoundException ("Felhasználónév és tartomány megadása kötelező"); } Felhasználó felhasználó = userRepository.findUser (felhasználónévAndDomain [0], felhasználónévAndDomain [1]); if (user == null) {dob új felhasználónévNotFoundException (String.format ("Felhasználónév nem található a domainhez, felhasználónév =% s, domain =% s", felhasználónévAndDomain [0], felhasználónévAndDomain [1])); } visszatérő felhasználó; }} 

3.3. Tavaszi biztonsági konfiguráció

Beállításunk különbözik a szokásos Spring Security konfigurációtól, mert beillesztjük a mi SimpleAuthenticationFilter a szűrő láncba az alapértelmezett előtt felhívással addFilterBefore:

A @Orride védett void konfiguráció (HttpSecurity http) a {http .addFilterBefore (authenticationFilter (), UsernamePasswordAuthenticationFilter.class) .authorizeRequests () .antMatchers ("/ css / **", "/ index"). SallAll () .antMatchers kivételt dobja ("/ user / **"). hitelesített () .és () .formLogin (). loginPage ("/ login") .and () .logout () .logoutUrl ("/ logout"); }

Használhatjuk a rendelkezésre bocsátottakat DaoAuthenticationProvider mert a miénkkel konfiguráljuk SimpleUserDetailsService. Emlékezz erre a mi SimpleUserDetailsService tudja, hogyan kell elemezni a mi felhasználónév és tartomány mezők és adja vissza a megfelelőt Felhasználó használható hitelesítéskor:

public AuthenticationProvider authProvider () {DaoAuthenticationProvider szolgáltató = új DaoAuthenticationProvider (); szolgáltató.setUserDetailsService (userDetailsService); szolgáltató.setPasswordEncoder (passwordEncoder ()); visszatérési szolgáltató; } 

Mivel a SimpleAuthenticationFilter, konfiguráljuk a sajátunkat AuthenticationFailureHandler a sikertelen bejelentkezési kísérletek megfelelő kezelésének biztosítása:

public SimpleAuthenticationFilter authenticationFilter () dobja a Kivételt {SimpleAuthenticationFilter filter = new SimpleAuthenticationFilter (); filter.setAuthenticationManager (authenticationManagerBean ()); filter.setAuthenticationFailureHandler (hibaHandler ()); visszatérő szűrő; }

3.4. Bejelentkezési oldal

Az általunk használt bejelentkezési oldal összegyűjti a kiegészítőinket tartomány mező, amelyet a mi SimpleAuthenticationFilter:

kérem jelentkezzen be

Példa: felhasználó / tartomány / jelszó

Érvénytelen felhasználó, jelszó vagy domain

Felhasználónév

Tartomány

Jelszó

Bejelentkezés

Vissza a honlapra

Amikor futtatjuk az alkalmazást, és hozzáférünk a kontextushoz a // localhost: 8081 címen, látunk egy linket a biztonságos oldal eléréséhez. A linkre kattintva megjelenik a bejelentkezési oldal. A várakozásoknak megfelelően látjuk a további tartomány mezőt:

3.5. Összegzés

Első példánkban újra felhasználhattuk DaoAuthenticationProvider és FelhasználónévPasswordAuthenticationToken a felhasználónév mező „hamisításával”.

Ennek eredményeként képesek voltunk adjon támogatást egy extra bejelentkezési mezőhöz, minimális mennyiségű konfigurációval és további kóddal.

4. Egyedi projektbeállítás

Második megközelítésünk nagyon hasonló lesz az elsőhöz, de alkalmasabb lehet nem triviális felhasználási esetekre.

Második megközelítésünk legfontosabb elemei a következők lesznek:

  • CustomAuthenticationFilterkiterjesztése FelhasználónévPasswordAuthenticationFilter
  • CustomUserDetailsServiceegyedi felület, amely deklarálja a loadUserbyUsernameAndDomain módszer
  • CustomUserDetailsServiceImplmegvalósításunk CustomUserDetailsService
  • CustomUserDetailsAuthenticationProviderkiterjesztése AbstractUserDetailsAuthenticationProvider
  • CustomAuthenticationTokenkiterjesztése FelhasználónévPasswordAuthenticationToken
  • Minketera. kiterjesztése Felhasználó osztály, amelyet a Spring Security biztosít, amely kijelenti az extránkat tartomány terület
  • SecurityConfigtavaszi biztonsági konfigurációnk, amely beilleszti a CustomAuthenticationFilter be a szűrő láncba, deklarálja a biztonsági szabályokat és összekapcsolja a függőségeket
  • login.htmla bejelentkezési oldal, amely összegyűjti a felhasználónév, Jelszó, és tartomány

4.1. Egyéni hitelesítési szűrő

Miénkben CustomAuthenticationFilter, mi bontsa ki a felhasználónév, jelszó és tartomány mezőket a kérésből. Ezeket az értékeket használjuk egyedi példányunk létrehozásáhozAuthenticationToken amelyet átadnak a AuthenticationProvider hitelesítéshez:

public class CustomAuthenticationFilter kiterjeszti a UsernamePasswordAuthenticationFilter {public static final String SPRING_SECURITY_FORM_DOMAIN_KEY = "domain"; @ A nyilvános hitelesítési kísérlet felülbírálása A hitelesítés (HttpServletRequest kérés, HttpServletResponse válasz) eldobja az AuthenticationException {// ... CustomAuthenticationToken authRequest = getAuthRequest (kérés); setDetails (kérés, authRequest); adja vissza ezt: getAuthenticationManager (). hitelesítés (authRequest); } privát CustomAuthenticationToken getAuthRequest (HttpServletRequest kérés) {String felhasználónév = getUsername (kérés); String jelszó = getPassword (kérés); Karakterlánc tartomány = getDomain (kérés); // ... return new CustomAuthenticationToken (felhasználónév, jelszó, tartomány); }

4.2. Egyedi UserDetails Szolgáltatás

A mi CustomUserDetailsService a szerződés egyetlen módszert határoz meg loadUserByUsernameAndDomain.

A CustomUserDetailsServiceImpl az általunk létrehozott osztály egyszerűen végrehajtja a szerződést és delegálja az CustomUserRepository hogy megkapja a Felhasználó:

 public UserDetails loadUserByUsernameAndDomain (String felhasználónév, String domain) dobja a UsernameNotFoundException {if (StringUtils.isAnyBlank (felhasználónév, domain)) {dob új felhasználónévNotFoundException ("Felhasználónév és tartomány megadása kötelező"); } Felhasználó felhasználó = userRepository.findUser (felhasználónév, tartomány); if (user == null) {dob új felhasználónévNotFoundException (String.format ("A domainhez nem található felhasználónév, felhasználónév =% s, domain =% s", felhasználónév, domain)); } visszatérő felhasználó; }

4.3. Egyedi UserDetailsAuthenticationProvider

A mi CustomUserDetailsAuthenticationProvider kiterjed AbstractUserDetailsAuthenticationProvider és delegálja a mi CustomUserDetailService letölteni a Felhasználó. Ennek az osztálynak a legfontosabb jellemzője a retrieveUser módszer.

Ne feledje, hogy át kell adnunk a hitelesítési tokent a miénkre CustomAuthenticationToken az egyéni mezőnk eléréséhez:

A @Orride védett UserDetails retrieveUser (karakterlánc felhasználónév, felhasználónévPasswordAuthenticationToken hitelesítés) dobja a AuthenticationException {CustomAuthenticationToken auth = (CustomAuthenticationToken) hitelesítést; UserDetails loadingUser; próbálja meg a {loadingUser = this.userDetailsService .loadUserByUsernameAndDomain (auth.getPrincipal () .toString (), auth.getDomain ()); } catch (UsernameNotFoundException notFound) {if (authentication.getCredentials ()! = null) {String presentPassword = hitelesítés.getCredentials () .toString (); passwordEncoder.matches (presentPassword, userNotFoundEncodedPassword); } dobás nem található; } catch (Exception repositoryProblem) {dobjon új InternalAuthenticationServiceException (repositoryProblem.getMessage (), repositoryProblem); } // ... return loadingUser; }

4.4. Összegzés

Második megközelítésünk közel azonos az előbb bemutatott egyszerű megközelítéssel. A sajátunk megvalósításával AuthenticationProvider és CustomAuthenticationToken, elkerültük a felhasználónév mezőt az egyéni elemzési logikával.

5. Következtetés

Ebben a cikkben egy űrlap bejelentkezést hajtottunk végre a Spring Security alkalmazásban, amely egy extra bejelentkezési mezőt használt. Ezt 2 különböző módon tettük meg:

  • Egyszerű megközelítésünkkel minimalizáltuk a szükséges kódmennyiséget. Képesek voltunk rá újrafelhasználás DaoAuthenticationProvider és UsernamePasswordAuthentication a felhasználónév módosításával egyedi elemzési logikával
  • Testreszabottabb megközelítésünkben egyedi terepi támogatást nyújtottunk az AbstractUserDetailsAuthenticationProvider kiterjesztése és sajátjaink biztosítása CustomUserDetailsService val,-vel CustomAuthenticationToken

Mint mindig, minden forráskód megtalálható a GitHubon.