Registracija - Aktivirajte novi račun e-poštom

Ovaj je članak dio serije: • Proljetni vodič za sigurnosnu registraciju

• Postupak registracije s proljetnom sigurnošću

• Registracija - aktivirajte novi račun e-poštom (trenutni članak) • Proljetna sigurnosna registracija - ponovno pošaljite e-poštu za potvrdu

• Registracija s Spring Security - kodiranje lozinkom

• API za registraciju postaje RESTful

• Proljetna sigurnost - resetirajte lozinku

• Registracija - Snaga lozinke i pravila

• Ažuriranje lozinke

1. Pregled

Ovaj članak nastavlja s tekućim Registracija u Spring Security niz s jednim od nedostajućih dijelova postupka registracije - provjera korisnikove e-pošte radi potvrde njegovog računa.

Mehanizam potvrde registracije prisiljava korisnika da odgovori na "Potvrdite registraciju”E-mail poslan nakon uspješne registracije radi potvrde njegove adrese e-pošte i aktiviranja njihovog računa. Korisnik to čini klikom na jedinstvenu vezu za aktivaciju koja mu se šalje e-poštom.

Slijedom ove logike, novo registrirani korisnik neće se moći prijaviti u sustav dok ovaj postupak ne bude dovršen.

2. Žeton za potvrdu

Koristit ćemo jednostavni token za provjeru kao ključni artefakt kojim se korisnik provjerava.

2.1. The VerificationToken Entitet

The VerificationToken subjekt mora ispunjavati sljedeće kriterije:

  1. Mora se vratiti na Korisnik (putem jednosmjerne relacije)
  2. Stvorit će se odmah nakon registracije
  3. Hoće ističu u roku od 24 sata slijedeći njegovo stvaranje
  4. Ima jedinstveno, nasumično generirano vrijednost

Zahtjevi 2 i 3 dio su logike registracije. Druga su dva implementirana na jednostavan način VerificationToken entitet poput onog u primjeru 2.1 .:

Primjer 2.1.

@Entity javna klasa VerificationToken {private static final int EXPIRATION = 60 * 24; @Id @GeneratedValue (strategija = GenerationType.AUTO) private Long id; privatni niz znakova; @OneToOne (targetEntity = User.class, fetch = FetchType.EAGER) @JoinColumn (nullable = false, name = "user_id") privatni korisnik; privatni Datum istekaDat; privatni datum izračunajExpiryDate (int expiryTimeInMinutes) {Calendar cal = Calendar.getInstance (); cal.setTime (nova vremenska oznaka (cal.getTime (). getTime ())); cal.add (Calendar.MINUTE, expiryTimeInMinutes); vrati novi datum (cal.getTime (). getTime ()); } // standardni konstruktori, getteri i postavljači}

Napomena nullable = false na Korisniku kako bi se osigurala cjelovitost i dosljednost podataka u VerificationToken <->Korisnik udruživanje.

2.2. Dodajte omogućeno Polje do Korisnik

U početku, kada je Korisnik je registriran, ovo omogućeno polje će biti postavljeno na lažno. Tijekom postupka potvrde računa - ako bude uspješan - postat će pravi.

Započnimo dodavanjem polja u naš Korisnik entitet:

korisnik javne klase {... @Column (name = "enabled") omogućen privatni logički podatak; javni korisnik () {super (); this.enabled = false; } ...}

Imajte na umu kako smo također postavili zadanu vrijednost ovog polja na lažno.

3. Tijekom registracije računa

Dodajmo dva dodatna dijela poslovne logike u slučaj upotrebe korisničke registracije:

  1. Generiraj VerificationToken za Korisnika i ustrajte na tome
  2. Pošaljite e-poruku za potvrdu računa - koja uključuje vezu za potvrdu s VerificationToken's vrijednost

3.1. Korištenje proljetnog događaja za izradu tokena i slanje e-pošte za potvrdu

Ova dva dodatna logička elementa upravljač ne bi trebao izvoditi izravno jer su to "kolateralni" pozadinski zadaci.

Kontrolor će objaviti Proljeće ApplicationEvent kako bi se pokrenulo izvršavanje ovih zadataka. Ovo je jednostavno kao ubrizgavanje ApplicationEventPublisher a zatim pomoću njega objaviti završetak registracije.

Primjer 3.1. pokazuje ovu jednostavnu logiku:

Primjer 3.1.

@Autowired ApplicationEventPublisher eventPublisher @PostMapping ("/ korisnik / registracija") public ModelAndView registerUserAccount (@ModelAttribute ("user") @Valid UserDto userDto, HttpServletRequest zahtjev, greške u pogreškama) {try {User registered = userService.regccountNewDDservice.regc Niz appUrl = request.getContextPath (); eventPublisher.publishEvent (novi OnRegistrationCompleteEvent (registriran, request.getLocale (), appUrl)); } catch (UserAlreadyExistException uaeEx) {ModelAndView mav = novi ModelAndView ("registracija", "korisnik", userDto); mav.addObject ("poruka", "Račun za to korisničko ime / e-adresu već postoji."); povratak mav; } catch (RuntimeException ex) {return new ModelAndView ("emailError", "user", userDto); } vratiti novi ModelAndView ("successRegister", "user", userDto); }

Još jednu stvar koju treba primijetiti je pokušaj uhvatiti blok oko objavljivanja događaja. Ovaj će kôd prikazati stranicu s pogreškom kad god postoji izuzetak u logici koja se izvršava nakon objavljivanja događaja, a to je u ovom slučaju slanje e-pošte.

3.2. Događaj i slušatelj

Pogledajmo sada stvarnu provedbu ovog novog OnRegistrationCompleteEvent koje naš kontroler šalje, kao i slušatelj koji će to riješiti:

Primjer 3.2.1. - The OnRegistrationCompleteEvent

javna klasa OnRegistrationCompleteEvent proširuje ApplicationEvent {private String appUrl; privatni lokalni jezik; privatni korisnik; javni OnRegistrationCompleteEvent (Korisnik korisnik, lokalizacija, String appUrl) {super (korisnik); this.user = korisnik; this.locale = locale; this.appUrl = appUrl; } // standardni getteri i postavljači}

Primjer 3.2.2. Slušatelj registracije Obrađuje OnRegistrationCompleteEvent

@Component javna klasa RegistrationListener implementira ApplicationListener {@Autowired private IUserService service; @Autowired privatne poruke MessageSource; @Autowired privatni JavaMailSender mailSender; @Override public void onApplicationEvent (OnRegistrationCompleteEvent event) {this.confirmRegistration (event); } privatna praznina confirmRegistration (događaj OnRegistrationCompleteEvent) {Korisnik korisnik = event.getUser (); Oznaka niza = UUID.randomUUID (). ToString (); service.createVerificationToken (korisnik, token); Niz primateljAddress = user.getEmail (); Niz predmeta = "Potvrda o registraciji"; Potvrda nizaUrl = event.getAppUrl () + "/regitrationConfirm.html?token=" + token; Niz poruke = messages.getMessage ("message.regSucc", null, event.getLocale ()); SimpleMailMessage e-mail = novi SimpleMailMessage (); email.setTo (primatelj Adresa); email.setSubject (subject); email.setText (poruka + "\ r \ n" + "// localhost: 8080" + potvrdaUrl); mailSender.send (email); }}

Evo, confirmRegistration metoda će dobiti OnRegistrationCompleteEvent, izvucite sve potrebno Korisnik podatke iz njega, stvorite token za provjeru, ustrajte u njemu, a zatim ga pošaljite kao parametar u "Potvrdite registraciju" veza.

Kao što je gore spomenuto, bilo koji javax.mail.AuthenticationFailedException bacio JavaMailSender upravljat će kontrolor.

3.3. Obrada parametra verifikacijskog tokena

Kada korisnik primi "Potvrdite registraciju"Link na koji bi trebali kliknuti.

Jednom kad to učine - kontroler će izvući vrijednost parametra tokena u rezultirajući GET zahtjev i upotrijebit će ga za omogućavanje Korisnik.

Pogledajmo ovaj postupak u primjeru 3.3.1 .:

Primjer 3.3.1. - RegistrationController Obrada potvrde o registraciji

@Autowired privatna usluga IUserService; @GetMapping ("/ regitrationConfirm") javni niz verifyRegistration (zahtjev WebRequest, model modela, @RequestParam ("token") Token string) {Locale locale = request.getLocale (); VerificationToken verifyToken = service.getVerificationToken (token); if (verifyToken == null) {String message = messages.getMessage ("auth.message.invalidToken", null, locale); model.addAttribute ("poruka", poruka); vrati "preusmjeravanje: /badUser.html? lang =" + locale.getLanguage (); } Korisnik korisnik = verifyToken.getUser (); Kalendar cal = Calendar.getInstance (); if ((verifyToken.getExpiryDate (). getTime () - cal.getTime (). getTime ()) <= 0) {String messageValue = messages.getMessage ("auth.message.expired", null, locale) model.addAttribute ("poruka", vrijednost poruke); vrati "preusmjeravanje: /badUser.html? lang =" + locale.getLanguage (); } user.setEnabled (true); service.saveRegisteredUser (korisnik); vrati "preusmjeravanje: /login.html? lang =" + request.getLocale (). getLanguage (); }

Korisnik će biti preusmjeren na stranicu pogreške s odgovarajućom porukom ako:

  1. The VerificationToken ne postoji, iz nekog razloga ili
  2. The VerificationToken istekao je

Vidi primjer 3.3.2. da biste vidjeli stranicu s pogreškom.

Primjer 3.3.2. - The badUser.html

Kao što vidimo, sada MyUserDetailsService ne koristi omogućeno zastava korisnika - i tako će omogućiti samo omogućenom korisniku da se provjeri autentičnost.

Sada ćemo dodati AuthenticationFailureHandler za prilagodbu poruka o iznimkama koje dolaze iz MyUserDetailsService. Naše CustomAuthenticationFailureHandler prikazan je u primjeru 4.2.:

Primjer 4.2. - CustomAuthenticationFailureHandler:

@Component javna klasa CustomAuthenticationFailureHandler proširuje SimpleUrlAuthenticationFailureHandler {@Autowired privatne poruke MessageSource; @Autowired private LocaleResolver localeResolver; @Override public void onAuthenticationFailure (HttpServletRequest zahtjev, HttpServletResponse odgovor, AuthenticationException izuzetak) baca IOException, ServletException {setDefaultFailureUrl ("/ login.html? Error = true"); super.onAuthenticationFailure (zahtjev, odgovor, iznimka); Locale locale = localeResolver.resolveLocale (zahtjev); String errorMessage = messages.getMessage ("message.badCredentials", null, locale); if (izuzet.getMessage (). equalsIgnoreCase ("Korisnik je onemogućen")) {errorMessage = messages.getMessage ("auth.message.disabled", null, locale); } else if (iznimka.getMessage (). equalsIgnoreCase ("Korisnički račun je istekao")) {errorMessage = messages.getMessage ("auth.message.expired", null, locale); } request.getSession (). setAttribute (WebAttributes.AUTHENTICATION_EXCEPTION, errorMessage); }}

Morat ćemo izmijeniti login.html za prikaz poruka o pogreškama.

Primjer 4.3. - Prikaži poruke o pogreškama na login.html:

 pogreška 

5. Prilagođavanje sloja postojanosti

Dajmo sada stvarnu provedbu nekih od ovih operacija koje uključuju token za provjeru, kao i korisnike.

Pokrivat ćemo:

  1. Nova VerificationTokenRepository
  2. Nove metode u IUserInterface i njegova primjena za nove CRUD operacije potrebne

Primjeri 5.1 - 5.3. pokazati nova sučelja i implementaciju:

Primjer 5.1. - The VerificationTokenRepository

javno sučelje VerificationTokenRepository proširuje JpaRepository {VerificationToken findByToken (niz znakova); VerificationToken findByUser (Korisnički korisnik); }

Primjer 5.2. - The IUserService Sučelje

javno sučelje IUserService {User registerNewUserAccount (UserDto userDto) baca UserAlreadyExistException; Korisnik getUser (String verifyToken); void saveRegisteredUser (Korisnik korisnik); void createVerificationToken (Korisnik korisnik, žeton žetona); VerificationToken getVerificationToken (String VerificationToken); }

Primjer 5.3. The Korisnička usluga

@Service @Transactional javna klasa UserService implementira IUserService {@Autowired privatno spremište UserRepository; @Autowired privatni VerificationTokenRepository tokenRepository; @Override public User registerNewUserAccount (UserDto userDto) baca UserAlreadyExistException {if (emailExist (userDto.getEmail ())) {throw new UserAlreadyExistException ("Postoji račun s tom adresom e-pošte:" + userDto.getEmail ()); } Korisnik korisnik = novi korisnik (); user.setFirstName (userDto.getFirstName ()); user.setLastName (userDto.getLastName ()); user.setPassword (userDto.getPassword ()); user.setEmail (userDto.getEmail ()); user.setRole (nova uloga (Integer.valueOf (1), korisnik)); vratiti spremište.save (korisnik); } privatni boolean emailExist (string e-pošte) {return userRepository.findByEmail (email)! = null; } @Override javni korisnik getUser (String verifyToken) {Korisnik korisnik = tokenRepository.findByToken (verifyToken) .getUser (); povratni korisnik; } @Override public VerificationToken getVerificationToken (String VerificationToken) {return tokenRepository.findByToken (VerificationToken); } @Override public void saveRegisteredUser (Korisnik korisnik) {repository.save (korisnik); } @Override public void createVerificationToken (Korisnik korisnik, žeton žetona) {VerificationToken myToken = novi VerificationToken (token, korisnik); tokenRepository.save (myToken); }}

6. Zaključak

U ovom smo članku proširili postupak registracije na sljedeći način postupak aktivacije računa temeljen na e-pošti.

Logika aktivacije računa zahtijeva slanje tokena za potvrdu korisniku putem e-pošte kako bi ga mogao poslati natrag kontroloru kako bi potvrdio svoj identitet.

Provedbu ovog vodiča za registraciju u Spring Security možete pronaći u projektu GitHub - ovo je projekt zasnovan na Eclipseu, pa bi ga trebalo lako uvesti i pokrenuti kakav jest.

Sljedeći » Proljetna sigurnosna registracija - ponovno pošaljite e-poštu za potvrdu « Prethodna Postupak registracije s proljetnom sigurnošću