Two Factor Auth med Spring Security

1. Oversigt

I denne vejledning vil vi implementere Two Factor Authentication-funktionalitet med en soft token og Spring Security.

Vi tilføjer den nye funktionalitet til et eksisterende, simpelt loginflow og bruger Google Authenticator-appen til at generere tokens.

Kort sagt, tofaktorautentificering er en verifikationsproces, der følger det velkendte princip om "noget, som brugeren ved, og noget, som brugeren har".

Og så giver brugerne et ekstra "verifikationstoken" under godkendelse - en engangskodebekræftelseskode baseret på tidsbaseret engangs-adgangskode TOTP-algoritme.

2. Maven-konfiguration

Først skal vi bruge Google Authenticator til vores app:

  • Generer hemmelig nøgle
  • Giv hemmelig nøgle til brugeren via QR-kode
  • Bekræft token indtastet af brugeren ved hjælp af denne hemmelige nøgle.

Vi bruger et simpelt bibliotek på serversiden til at generere / verificere engangskodeord ved at tilføje følgende afhængighed til vores pom.xml:

 org.jboss.aerogear aerogear-otp-java 1.0.0 

3. Brugerenhed

Dernæst vil vi ændre vores brugerenhed for at holde ekstra information - som følger:

@Entity offentlig klasse bruger {... privat boolsk isUsing2FA; privat String hemmelighed; offentlig bruger () {super (); this.secret = Base32.random (); ...}}

Noter det:

  • Vi gemmer en tilfældig hemmelig kode til hver bruger, der senere skal bruges til at generere verifikationskode
  • Vores totrinsbekræftelse er valgfri

4. Ekstra loginparameter

Først skal vi justere vores sikkerhedskonfiguration for at acceptere ekstra parameter - verifikationstoken. Vi kan opnå det ved at bruge brugerdefineret AuthenticationDetailsSource:

Her er vores CustomWebAuthenticationDetailsSource:

@Komponent offentlig klasse CustomWebAuthenticationDetailsSource implementerer AuthenticationDetailsSource {@Override public WebAuthenticationDetails buildDetails (HttpServletRequest context) {returner nye CustomWebAuthenticationDetails (kontekst); }}

og her er CustomWebAuthenticationDetails:

offentlig klasse CustomWebAuthenticationDetails udvider WebAuthenticationDetails {private String verificationCode; offentlige CustomWebAuthenticationDetails (HttpServletRequest anmodning) {super (anmodning); verificationCode = request.getParameter ("kode"); } offentlig String getVerificationCode () {return verificationCode; }}

Og vores sikkerhedskonfiguration:

@Configuration @EnableWebSecurity offentlig klasse LssSecurityConfig udvider WebSecurityConfigurerAdapter {@Autowired private CustomWebAuthenticationDetailsSource authenticationDetailsSource; @ Override beskyttet ugyldig konfiguration (HttpSecurity http) kaster undtagelse {http.formLogin () .authenticationDetailsSource (authenticationDetailsSource) ...}}

Og til sidst tilføj den ekstra parameter til vores loginformular:

 Google Authenticator-bekræftelseskode 

Bemærk: Vi er nødt til at indstille vores brugerdefinerede AuthenticationDetailsSource i vores sikkerhedskonfiguration.

5. Tilpasset godkendelsesudbyder

Dernæst har vi brug for en brugerdefineret AuthenticationProvider til at håndtere ekstra parametervalidering:

offentlig klasse CustomAuthenticationProvider udvider DaoAuthenticationProvider {@Autowired private UserRepository userRepository; @Override public Authentication authenticate (Authentication auth) kaster AuthenticationException {String verificationCode = ((CustomWebAuthenticationDetails) auth.getDetails ()) .getVerificationCode (); Brugerbruger = userRepository.findByEmail (auth.getName ()); if ((user == null)) {throw new BadCredentialsException ("Ugyldigt brugernavn eller adgangskode"); } hvis (user.isUsing2FA ()) {Totp totp = ny Totp (user.getSecret ()); hvis (! isValidLong (verificationCode) ||! totp.verify (verificationCode)) {kast ny BadCredentialsException ("Ugyldig verifikationskode"); }} Autentificeringsresultat = super.authenticate (auth); returner nyt UsernamePasswordAuthenticationToken (bruger, result.getCredentials (), result.getAuthorities ()); } privat boolsk isValidLong (strengkode) {prøv {Long.parseLong (kode); } fange (NumberFormatException e) {return false; } returner sandt } @Override offentlige booleske understøttelser (klasseautentificering) {return authentication.equals (UsernamePasswordAuthenticationToken.class); }}

Bemærk, at - efter at vi har verificeret den ene gangs adgangskodebekræftelseskode, delegerede vi simpelthen godkendelse nedstrøms.

Her er vores Authentication Provider-bønne

@Bean offentlig DaoAuthenticationProvider authProvider () {CustomAuthenticationProvider authProvider = ny CustomAuthenticationProvider (); authProvider.setUserDetailsService (userDetailsService); authProvider.setPasswordEncoder (encoder ()); returner authProvider; }

6. Registreringsproces

For at brugerne skal kunne bruge applikationen til at generere tokens, skal de nu konfigurere tingene korrekt, når de registrerer sig.

Og så bliver vi nødt til at foretage nogle få enkle ændringer af registreringsprocessen - for at tillade brugere, der har valgt at bruge totrinsbekræftelse, at scann den QR-kode, de har brug for for at logge ind senere.

Først tilføjer vi dette enkle input til vores registreringsformular:

Brug totrinsbekræftelse 

Så i vores RegistrationController - vi omdirigerer brugere baseret på deres valg efter bekræftelse af registrering:

@GetMapping ("/ registrationConfirm") offentlig streng bekræftelsesregistrering (@RequestParam ("token") String token, ...) {String result = userService.validateVerificationToken (token); hvis (result.equals ("valid")) {User user = userService.getUser (token); hvis (user.isUsing2FA ()) {model.addAttribute ("qr", userService.generateQRUrl (bruger)); return "redirect: /qrcode.html? lang =" + locale.getLanguage (); } model.addAttribute ("besked", messages.getMessage ("message.accountVerified", null, locale)); returner "redirect: / login? lang =" + locale.getLanguage (); } ...}

Og her er vores metode generereQRUrl ():

offentlig statisk streng QR_PREFIX = "//chart.googleapis.com/chart?chs=200x200&chld=M%%7C0&cht=qr&chl="; @ Overstyr offentlig streng generereQRUrl (brugerbruger) {returner QR_PREFIX + URLEncoder.encode (String.format ("otpauth: // totp /% s:% s? Secret =% s & issuer =% s", APP_NAME, user.getEmail () , user.getSecret (), APP_NAME), "UTF-8"); }

Og her er vores qrcode.html:

Scan denne stregkode ved hjælp af Google Authenticator-appen på din telefon for at bruge den senere i login

Gå til login-siden

Noter det:

  • generereQRUrl () metoden bruges til at generere QR-kode URL
  • Denne QR-kode scannes af brugerens mobiltelefoner ved hjælp af Google Authenticator-appen
  • Appen genererer en 6-cifret kode, der kun er gyldig i 30 sekunder, hvilket er den ønskede verifikationskode
  • Denne verifikationskode vil blive verificeret under login ved hjælp af vores brugerdefinerede AuthenticationProvider

7. Aktivér totrinsbekræftelse

Dernæst sørger vi for, at brugerne når som helst kan ændre deres loginindstillinger - som følger:

@PostMapping ("/ bruger / opdatering / 2fa") offentlig GenericResponse modifyUser2FA (@RequestParam ("use2FA") boolsk use2FA) kaster UnsupportedEncodingException {User user = userService.updateUser2FA (use2FA); if (use2FA) {return new GenericResponse (userService.generateQRUrl (user)); } returnere null; }

Og her er updateUser2FA ():

@Override offentlig brugeropdateringUser2FA (boolsk use2FA) {Authentication curAuth = SecurityContextHolder.getContext (). GetAuthentication (); Bruger currentUser = (bruger) curAuth.getPrincipal (); currentUser.setUsing2FA (use2FA); currentUser = repository.save (currentUser); Authentication auth = new UsernamePasswordAuthenticationToken (currentUser, currentUser.getPassword (), curAuth.getAuthorities ()); SecurityContextHolder.getContext (). SetAuthentication (auth); returstrøm Bruger; }

Og her er frontend:

 Du bruger totrinsgodkendelse Deaktiver 2FA Du bruger ikke totrinsgodkendelse Aktivér 2FA

Scan denne stregkode ved hjælp af Google Authenticator-appen på din telefon

funktion enable2FA () {set2FA (true); } funktion deaktiver2FA () {set2FA (falsk); } funktion set2FA (use2FA) {$ .post ("/ user / update / 2fa", {use2FA: use2FA}, function (data) {if (use2FA) {$ ("# qr"). append (''). vis ();} andet {window.location.reload ();}}); }

8. Konklusion

I denne hurtige vejledning illustrerede vi, hvordan du udfører en to-faktor-godkendelsesimplementering ved hjælp af en soft token med Spring Security.

Den fulde kildekode kan findes - som altid - over på GitHub.


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