Registrering - Aktiver en ny konto via e-mail

Denne artikel er en del af en serie: • Vejledning i Spring Security Registration

• Registreringsprocessen med forårssikkerhed

• Registrering - Aktiver en ny konto via e-mail (nuværende artikel) • Forårsikkerhedsregistrering - Send bekræftelses-e-mail igen

• Registrering med Spring Security - kodning af adgangskode

• Registrerings-API'en bliver RESTful

• Spring Security - Nulstil din adgangskode

• Registrering - adgangskodestyrke og regler

• Opdatering af din adgangskode

1. Oversigt

Denne artikel fortsætter den igangværende Registrering hos Spring Security serie med en af ​​de manglende dele af registreringsprocessen - bekræftelse af brugerens e-mail for at bekræfte deres konto.

Registreringsbekræftelsesmekanismen tvinger brugeren til at svare på en “Bekræft tilmelding”E-mail sendt efter vellykket registrering for at bekræfte hans e-mail-adresse og aktivere deres konto. Brugeren gør dette ved at klikke på et unikt aktiveringslink sendt til dem via e-mail.

Efter denne logik kan en nyregistreret bruger ikke logge ind på systemet, før denne proces er afsluttet.

2. En verifikationstoken

Vi bruger et simpelt verifikationstoken som nøgleartefakt, gennem hvilken en bruger verificeres.

2.1. Det VerificationToken Enhed

Det VerificationToken enhed skal opfylde følgende kriterier:

  1. Det skal linke tilbage til Bruger (via et ensrettet forhold)
  2. Det oprettes lige efter registrering
  3. Det vil udløber inden for 24 timer efter dets oprettelse
  4. Har en unik, tilfældigt genereret værdi

Krav 2 og 3 er en del af registreringslogikken. De to andre er implementeret på en enkel måde VerificationToken enhed som den i eksempel 2.1:

Eksempel 2.1.

@Entity public class VerificationToken {privat statisk endelig int EXPIRATION = 60 * 24; @Id @GeneratedValue (strategi = GenerationType.AUTO) privat Lang id; privat String token; @OneToOne (targetEntity = User.class, fetch = FetchType.EAGER) @JoinColumn (nullable = false, name = "user_id") privat brugerbruger; privat Dato udløbsdato; privat dato calcExpiryDate (int expiryTimeInMinutes) {Kalender cal = Calendar.getInstance (); cal.setTime (ny tidsstempel (cal.getTime (). getTime ())); cal.add (Calendar.MINUTE, expiryTimeInMinutes); returner ny dato (cal.getTime (). getTime ()); } // standardkonstruktører, getters og settere}

Bemærk nullable = false på brugeren for at sikre dataintegritet og konsistens i VerificationToken <->Bruger forening.

2.2. Tilføj aktiveret Mark til Bruger

Oprindeligt, da Bruger er registreret, dette aktiveret felt vil blive indstillet til falsk. Under kontobekræftelsesprocessen - hvis det lykkes - bliver det rigtigt.

Lad os starte med at tilføje feltet til vores Bruger enhed:

offentlig klasse bruger {... @Column (name = "enabled") privat boolsk aktiveret; offentlig bruger () {super (); this.enabled = false; } ...}

Bemærk, hvordan vi også indstiller standardværdien for dette felt til falsk.

3. Under kontoregistrering

Lad os tilføje yderligere to stykker forretningslogik til brugerregistreringen:

  1. Generer VerificationToken for brugeren og fortsætte den
  2. Send e-mail-beskeden til kontobekræftelse - som inkluderer et bekræftelseslink med VerificationToken's værdi

3.1. Brug af en forårsbegivenhed til at oprette tokenet og sende bekræftelses-e-mailen

Disse to ekstra stykker logik bør ikke udføres af controlleren direkte, fordi de er "sikkerhedsstillede" back-end-opgaver.

Controlleren vil udgive et forår ApplicationEvent for at udløse udførelsen af ​​disse opgaver. Dette er så simpelt som at injicere ApplicationEventPublisher og derefter bruge den til at offentliggøre afslutningen af ​​registreringen.

Eksempel 3.1. viser denne enkle logik:

Eksempel 3.1.

@Autowired ApplicationEventPublisher eventPublisher @PostMapping ("/ user / registration") public ModelAndView registerUserAccount (@ModelAttribute ("user") @Valid UserDto userDto, HttpServletRequest anmodning, Fejlfejl) {prøv {Brugerregistreret = userService.registerNewUserA String appUrl = request.getContextPath (); eventPublisher.publishEvent (nyt OnRegistrationCompleteEvent (registreret, anmodning.getLocale (), appUrl)); } fange (UserAlreadyExistException uaeEx) {ModelAndView mav = ny ModelAndView ("registrering", "bruger", userDto); mav.addObject ("meddelelse", "Der findes allerede en konto for dette brugernavn / e-mail."); returnere mav; } fange (RuntimeException ex) {returner ny ModelAndView ("emailError", "bruger", userDto); } returner ny ModelAndView ("successRegister", "user", userDto); }

En yderligere ting at bemærke er prøv at fange blok omkring udgivelsen af ​​begivenheden. Dette stykke kode viser en fejlside, hver gang der er en undtagelse i logikken, der udføres efter offentliggørelsen af ​​begivenheden, hvilket i dette tilfælde er afsendelsen af ​​e-mailen.

3.2. Begivenheden og lytteren

Lad os nu se den faktiske implementering af denne nye OnRegistrationCompleteEvent at vores controller sender ud, såvel som lytteren, der skal håndtere det:

Eksempel 3.2.1. - Det OnRegistrationCompleteEvent

offentlig klasse OnRegistrationCompleteEvent udvider ApplicationEvent {private String appUrl; private lokale lokaliteter; privat brugerbruger; offentlig OnRegistrationCompleteEvent (brugerbruger, landestandard, streng appUrl) {super (bruger); this.user = bruger; this.locale = lokalitet; this.appUrl = appUrl; } // standard getters og setters}

Eksempel 3.2.2. RegistrationListener Håndterer OnRegistrationCompleteEvent

@Komponent offentlig klasse RegistrationListener implementerer ApplicationListener {@Autowired privat IUserService-tjeneste; @Autowired private MessageSource beskeder; @Autowired privat JavaMailSender mailSender; @ Overstyr offentlig ugyldighed påApplicationEvent (OnRegistrationCompleteEvent event) {this.confirmRegistration (event); } privat ugyldig confirmRegistration (OnRegistrationCompleteEvent begivenhed) {Bruger bruger = event.getUser (); String token = UUID.randomUUID (). ToString (); service.createVerificationToken (bruger, token); String recipientAddress = user.getEmail (); Strengemne = "Registreringsbekræftelse"; StrengbekræftelseUrl = event.getAppUrl () + "/regitrationConfirm.html?token=" + token; Strengmeddelelse = messages.getMessage ("message.regSucc", null, event.getLocale ()); SimpleMailMessage email = ny SimpleMailMessage (); email.setTo (recipientAddress); email.setSubject (emne); email.setText (besked + "\ r \ n" + "// localhost: 8080" + bekræftelse Url); mailSender.send (e-mail); }}

Her, den Bekræft tilmelding metoden modtager OnRegistrationCompleteEvent, træk alt det nødvendige ud Bruger oplysninger fra det, opret verifikationstokenet, vedvar det og send det derefter som en parameter i “Bekræft tilmelding" link.

Som nævnt ovenfor, nogen javax.mail.AuthenticationFailedException kastet af JavaMailSender håndteres af controlleren.

3.3. Behandling af parameteren for bekræftelsestoken

Når brugeren modtager “Bekræft tilmelding”Link skal de klikke på det.

Når de gør det - udtager controlleren værdien af ​​tokenparameteren i den resulterende GET-anmodning og bruger den til at aktivere Bruger.

Lad os se denne proces i eksempel 3.3.1 .:

Eksempel 3.3.1. - RegistrationController Behandler registreringsbekræftelsen

@Autowired privat IUserService-tjeneste; @GetMapping ("/ regitrationConfirm") public String confirmRegistration (WebRequest request, Model model, @RequestParam ("token") String token) {Locale locale = request.getLocale (); VerificationToken verificationToken = service.getVerificationToken (token); if (verificationToken == null) {String message = messages.getMessage ("auth.message.invalidToken", null, locale); model.addAttribute ("besked", besked); returner "redirect: /badUser.html? lang =" + locale.getLanguage (); } Brugerbruger = verificationToken.getUser (); Kalender cal = Calendar.getInstance (); if ((verificationToken.getExpiryDate (). getTime () - cal.getTime (). getTime ()) <= 0) {String messageValue = messages.getMessage ("auth.message.expired", null, locale) model.addAttribute ("besked", messageValue); returner "redirect: /badUser.html? lang =" + locale.getLanguage (); } user.setEnabled (sand); service.saveRegisteredUser (bruger); returner "redirect: /login.html? lang =" + request.getLocale (). getLanguage (); }

Brugeren omdirigeres til en fejlside med den tilsvarende meddelelse, hvis:

  1. Det VerificationToken eksisterer ikke af en eller anden grund eller
  2. Det VerificationToken er udløbet

Se eksempel 3.3.2. for at se fejlsiden.

Eksempel 3.3.2. - Det badUser.html

Som vi kan se, nu MyUserDetailsService bruger ikke aktiveret brugerens flag - og så tillader det kun aktiveret brugeren at autentificere.

Nu vil vi tilføje en AuthenticationFailureHandler for at tilpasse undtagelsesmeddelelserne fra MyUserDetailsService. Vores CustomAuthenticationFailureHandler er vist i eksempel 4.2.:

Eksempel 4.2. - CustomAuthenticationFailureHandler:

@Komponent offentlig klasse CustomAuthenticationFailureHandler udvider SimpleUrlAuthenticationFailureHandler {@Autowired private MessageSource-meddelelser; @Autowired privat LocaleResolver localeResolver; @Override offentlig ugyldighed onAuthenticationFailure (HttpServletRequest anmodning, HttpServletResponse svar, AuthenticationException undtagelse) kaster IOException, ServletException {setDefaultFailureUrl ("/ login.html? Error = true"); super.onAuthenticationFailure (anmodning, svar, undtagelse); Lokal locale = localeResolver.resolveLocale (anmodning); Streng errorMessage = messages.getMessage ("message.badCredentials", null, locale); if (exception.getMessage (). equalsIgnoreCase ("Bruger er deaktiveret")) {errorMessage = messages.getMessage ("auth.message.disabled", null, locale); } ellers hvis (exception.getMessage (). equalsIgnoreCase ("Brugerkonto er udløbet")) {errorMessage = messages.getMessage ("auth.message.expired", null, locale); } request.getSession (). setAttribute (WebAttributs.AUTHENTICATION_EXCEPTION, errorMessage); }}

Vi bliver nødt til at ændre login.html for at vise fejlmeddelelserne.

Eksempel 4.3. - Vis fejlmeddelelser kl login.html:

 fejl 

5. Tilpasning af Persistence Layer

Lad os nu give den faktiske implementering af nogle af disse operationer, der involverer verifikationstokenet såvel som brugerne.

Vi dækker:

  1. Et nyt VerificationTokenRepository
  2. Nye metoder i IUserInterface og implementeringen af ​​den nødvendige CRUD-operation

Eksempel 5.1 - 5.3. Vis de nye grænseflader og implementering:

Eksempel 5.1. - Det VerificationTokenRepository

offentlig grænseflade VerificationTokenRepository udvider JpaRepository {VerificationToken findByToken (String token); VerificationToken findByUser (brugerbruger); }

Eksempel 5.2. - Det IUserService Interface

offentlig grænseflade IUserService {User registerNewUserAccount (UserDto userDto) kaster UserAlreadyExistException; User getUser (String verificationToken); ugyldig saveRegisteredUser (brugerbruger); ugyldigt createVerificationToken (brugerbruger, streng-token); VerificationToken getVerificationToken (String VerificationToken); }

Eksempel 5.3. Det UserService

@Service @Transactional public class UserService implementerer IUserService {@Autowired privat UserRepository repository; @Autowired privat VerificationTokenRepository tokenRepository; @Override offentligt brugerregisterNyUserAccount (UserDto brugerDto) kaster UserAlreadyExistException {if (emailExist (userDto.getEmail ())) {throw new UserAlreadyExistException ("Der er en konto med den e-mail-adresse:" + userDto.getEmail ()); } Brugerbruger = ny bruger (); user.setFirstName (userDto.getFirstName ()); user.setLastName (userDto.getLastName ()); user.setPassword (userDto.getPassword ()); user.setEmail (userDto.getEmail ()); user.setRole (ny rolle (Integer.valueOf (1), bruger)); returner repository.save (bruger); } privat boolsk emailExist (String email) {return userRepository.findByEmail (email)! = null; } @ Override public User getUser (String verificationToken) {User user = tokenRepository.findByToken (verificationToken) .getUser (); tilbagevendende bruger } @Override public VerificationToken getVerificationToken (String VerificationToken) {return tokenRepository.findByToken (VerificationToken); } @ Overstyr offentlig tomrum saveRegisteredUser (brugerbruger) {repository.save (bruger); } @ Overstyr offentlig ugyldighed createVerificationToken (brugerbruger, streng-token) {VerificationToken myToken = nyt VerificationToken (token, bruger); tokenRepository.save (myToken); }}

6. Konklusion

I denne artikel har vi udvidet registreringsprocessen til at omfatte en e-mail-baseret aktivering af konto.

Kontoaktiveringslogikken kræver, at der sendes et bekræftelsestoken til brugeren via e-mail, så de kan sende det tilbage til controlleren for at bekræfte deres identitet.

Implementeringen af ​​denne registrering med Spring Security-vejledning kan findes i GitHub-projektet - dette er et Eclipse-baseret projekt, så det skal være let at importere og køre som det er.

Næste » Forårsikkerhedsregistrering - Send bekræftelses-e-mail igen « Tidligere Registreringsprocessen med forårssikkerhed

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