Dvojfaktorové overenie s jarnou bezpečnosťou

1. Prehľad

V tomto výučbe budeme implementovať funkčnosť dvojfaktorového overovania pomocou mäkkého tokenu a jarnej bezpečnosti.

Budeme pridávať nové funkcie do existujúceho jednoduchého toku prihlásenia a na generovanie tokenov použijeme aplikáciu Google Authenticator.

Zjednodušene povedané, dvojfaktorová autentifikácia je proces overovania, ktorý sa riadi dobre známym princípom „niečo, čo užívateľ vie, a niečo, čo užívateľ má“.

Používatelia teda počas autentifikácie poskytujú ďalší „overovací token“ - jednorazový kód na overenie hesla založený na algoritme Time-based One-time Password TOTP.

2. Konfigurácia Maven

Najskôr, aby sme mohli aplikáciu Google Authenticator používať v našej aplikácii, musíme:

  • Vygenerujte tajný kľúč
  • Poskytnite používateľovi tajný kľúč prostredníctvom QR kódu
  • Pomocou tohto tajného kľúča overte token zadaný používateľom.

Použijeme jednoduchú knižnicu na strane servera na vygenerovanie / overenie jednorazového hesla pridaním nasledujúcej závislosti do našej pom.xml:

 org.jboss.aerogear aerogear-otp-java 1.0.0 

3. Entita používateľa

Ďalej upravíme našu používateľskú entitu tak, aby obsahovala ďalšie informácie, a to nasledovne:

@Entity verejná trieda Používateľ {... private boolean isUsing2FA; súkromné ​​reťazcové tajomstvo; public User () {super (); this.secret = Base32.random (); ...}}

Poznač si to:

  • Ukladáme náhodný tajný kód pre každého používateľa, ktorý sa použije neskôr pri generovaní overovacieho kódu
  • Naše dvojstupňové overenie je voliteľné

4. Extra parameter prihlásenia

Najskôr budeme musieť upraviť našu konfiguráciu zabezpečenia tak, aby akceptovala ďalší parameter - overovací token. To môžeme dosiahnuť použitím zvyku AuthenticationDetailsSource:

Tu je náš CustomWebAuthenticationDetailsSource:

@ Verejná trieda komponentu CustomWebAuthenticationDetailsSource implementuje AuthenticationDetailsSource {@Override public WebAuthenticationDetails buildDetails (kontext HttpServletRequest) {vrátiť nový CustomWebAuthenticationDetails (kontext); }}

a tu je CustomWebAuthenticationDetails:

verejná trieda CustomWebAuthenticationDetails rozširuje WebAuthenticationDetails {private String verificationCode; public CustomWebAuthenticationDetails (požiadavka HttpServletRequest) {super (požiadavka); verificationCode = request.getParameter ("kód"); } public String getVerificationCode () {návrat overovací kód; }}

A naša konfigurácia zabezpečenia:

@Configuration @EnableWebSecurity verejná trieda LssSecurityConfig rozširuje WebSecurityConfigurerAdapter {@Autowired private CustomWebAuthenticationDetailsSource authenticationDetailsSource; @Override protected void configure (HttpSecurity http) vyvolá výnimku {http.formLogin () .authenticationDetailsSource (authenticationDetailsSource) ...}}

A nakoniec pridajte ďalší parameter do nášho prihlasovacieho formulára:

 Verifikačný kód aplikácie Google Authenticator 

Poznámka: Musíme nastaviť náš zvyk AuthenticationDetailsSource v našej konfigurácii zabezpečenia.

5. Vlastný poskytovateľ autentifikácie

Ďalej budeme potrebovať zvyk AuthenticationProvider na spracovanie overenia ďalších parametrov:

verejná trieda CustomAuthenticationProvider rozširuje DaoAuthenticationProvider {@Autowired private UserRepository userRepository; @Override public Authentication authenticate (Authentication auth) throws AuthenticationException {String verificationCode = ((CustomWebAuthenticationDetails) auth.getDetails ()) .getVerificationCode (); Používateľ user = userRepository.findByEmail (auth.getName ()); if ((user == null)) {throw new BadCredentialsException ("Invalid username or password"); } if (user.isUsing2FA ()) {Totp totp = nový Totp (user.getSecret ()); if (! isValidLong (verificationCode) ||! totp.verify (verificationCode)) {throw new BadCredentialsException ("Invalid verfication code"); }} Výsledok autentifikácie = super.authenticate (auth); vrátiť nové UsernamePasswordAuthenticationToken (user, result.getCredentials (), result.getAuthorities ()); } private boolean isValidLong (reťazcový kód) {try {Long.parseLong (kód); } catch (NumberFormatException e) {return false; } návrat pravdivý; } @Override public boolean podporuje (autentifikácia triedy) {návrat autentifikácie.equals (UsernamePasswordAuthenticationToken.class); }}

Upozorňujeme, že - keď sme overili jednorazový overovací kód hesla, jednoducho sme delegovali autentifikáciu po prúde.

Tu je naša fazuľa poskytovateľa autentifikácie

@Bean public DaoAuthenticationProvider authProvider () {CustomAuthenticationProvider authProvider = nový CustomAuthenticationProvider (); authProvider.setUserDetailsService (userDetailsService); authProvider.setPasswordEncoder (encoder ()); návrat authProvider; }

6. Proces registrácie

Teraz, aby používatelia mohli používať aplikáciu na generovanie tokenov, budú musieť pri registrácii správne nastaviť veci.

Budeme teda musieť urobiť niekoľko jednoduchých úprav procesu registrácie - aby sme používateľom, ktorí sa rozhodli použiť dvojstupňové overenie, umožnili naskenujte QR kód, ktorý potrebujú na neskoršie prihlásenie.

Najskôr pridáme tento jednoduchý vstup do nášho registračného formulára:

Použite dvojstupňové overenie 

Potom v našom RegistrationController - používateľov presmerujeme na základe ich výberu po potvrdení registrácie:

@GetMapping ("/ registrationConfirm") public String confirmRegistration (@RequestParam ("token") String token, ...) {String result = userService.validateVerificationToken (token); if (result.equals ("valid")) {User user = userService.getUser (token); if (user.isUsing2FA ()) {model.addAttribute ("qr", userService.generateQRUrl (užívateľ)); return "redirect: /qrcode.html? lang =" + locale.getLanguage (); } model.addAttribute ("správa", messages.getMessage ("message.accountVerified", null, miestne nastavenie)); return "redirect: / login? lang =" + locale.getLanguage (); } ...}

A tu je naša metóda generateQRUrl ():

verejný statický reťazec QR_PREFIX = "//chart.googleapis.com/chart?chs=200x200&chld=M%%7C0&cht=qr&chl="; @Override public String generateQRUrl (užívateľský užívateľ) {return QR_PREFIX + URLEncoder.encode (String.format ("otpauth: // totp /% s:% s? Secret =% s & issuer =% s", APP_NAME, user.getEmail () , user.getSecret (), APP_NAME), „UTF-8“); }

A tu je náš qrcode.html:

Naskenujte tento čiarový kód pomocou aplikácie Google Authenticator na telefóne, aby ste ho mohli neskôr použiť pri prihlasovaní

Prejdite na prihlasovaciu stránku

Poznač si to:

  • generateQRUrl () metóda sa používa na generovanie URL kódu QR
  • Tento QR kód naskenujú používatelia mobilných telefónov pomocou aplikácie Google Authenticator
  • Aplikácia vygeneruje šesťmiestny kód, ktorý je platný iba 30 sekúnd, čo je požadovaný overovací kód
  • Tento verifikačný kód bude overený pri prihlásení pomocou nášho zvyku AuthenticationProvider

7. Povolte dvojstupňové overenie

Ďalej zaistíme, aby používatelia mohli kedykoľvek zmeniť svoje predvoľby prihlásenia - a to nasledovne:

@PostMapping ("/ user / update / 2fa") public GenericResponse modifyUser2FA (@RequestParam ("use2FA") boolean use2FA) hodí UnsupportedEncodingException {User user = userService.updateUser2FA (use2FA); if (use2FA) {return new GenericResponse (userService.generateQRUrl (user)); } return null; }

A tu je updateUser2FA ():

@Override public User updateUser2FA (boolean use2FA) {Authentication curAuth = SecurityContextHolder.getContext (). GetAuthentication (); Užívateľ currentUser = (Užívateľ) curAuth.getPrincipal (); currentUser.setUsing2FA (use2FA); currentUser = repository.save (currentUser); Autentifikácia auth = nové UsernamePasswordAuthenticationToken (currentUser, currentUser.getPassword (), curAuth.getAuthorities ()); SecurityContextHolder.getContext (). SetAuthentication (auth); návratový prúdUser; }

A tu je front-end:

 Používate dvojstupňové overenie Zakázať 2FA Nepoužívate dvojstupňové overenie Povoliť 2FA

Naskenujte tento čiarový kód pomocou aplikácie Google Authenticator v telefóne

funkcia enable2FA () {set2FA (true); } funkcia disable2FA () {set2FA (false); } function set2FA (use2FA) {$ .post ("/ user / update / 2fa", {use2FA: use2FA}, function (data) {if (use2FA) {$ ("# qr"). append (''). show ();} else {window.location.reload ();}}); }

8. Záver

V tomto rýchlom výučbe sme si ukázali, ako vykonať implementáciu dvojfaktorovej autentifikácie pomocou mäkkého tokenu s jarnou bezpečnosťou.

Celý zdrojový kód nájdete - ako vždy - na GitHub.


$config[zx-auto] not found$config[zx-overlay] not found