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:
- SimpleAuthenticationFilter – kiterjesztése FelhasználónévPasswordAuthenticationFilter
- SimpleUserDetailsService – megvalósítása UserDetailsService
- Minketer – a. kiterjesztése Felhasználó osztály, amelyet a Spring Security biztosít, amely kijelenti az extránkat tartomány terület
- SecurityConfig – tavaszi 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.html– egy 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ésVissza 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:
- CustomAuthenticationFilter – kiterjesztése FelhasználónévPasswordAuthenticationFilter
- CustomUserDetailsService – egyedi felület, amely deklarálja a loadUserbyUsernameAndDomain módszer
- CustomUserDetailsServiceImpl – megvalósításunk CustomUserDetailsService
- CustomUserDetailsAuthenticationProvider – kiterjesztése AbstractUserDetailsAuthenticationProvider
- CustomAuthenticationToken – kiterjesztése FelhasználónévPasswordAuthenticationToken
- Minketer – a. kiterjesztése Felhasználó osztály, amelyet a Spring Security biztosít, amely kijelenti az extránkat tartomány terület
- SecurityConfig – tavaszi 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.html– a 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.