Het registratieproces met Spring Security

Dit artikel maakt deel uit van een reeks: • Tutorial Spring Security Registration

• Het registratieproces met Spring Security (huidig ​​artikel) • Registratie - Activeer een nieuw account per e-mail

• Spring Security Registration - Verzend de verificatie-e-mail opnieuw

• Registratie met Spring Security - Wachtwoordcodering

• De registratie-API wordt RESTful

• Spring Security - Reset uw wachtwoord

• Registratie - Wachtwoordsterkte en regels

• Uw wachtwoord bijwerken

1. Overzicht

In dit artikel implementeren we een basisregistratieproces met Spring Security. Dit bouwt voort op de concepten die in het vorige artikel zijn onderzocht, waar we naar inloggen hebben gekeken.

Het doel is hier om toe te voegen een volledig registratieproces waarmee een gebruiker zich kan aanmelden, gebruikersgegevens valideert en behoudt.

2. De registratiepagina

Ten eerste: laten we een eenvoudige registratiepagina implementeren die weergeeft de volgende velden:

  • naam (voornaam en achternaam)
  • e-mail
  • wachtwoord (en wachtwoord bevestigingsveld)

Het volgende voorbeeld toont een eenvoudig registration.html bladzijde:

Voorbeeld 2.1.

het formulier

eerste

Validatiefout

laatste

Validatiefout

e-mail

Validatiefout

wachtwoord

Validatiefout

bevestig indienen login

3.Het gebruiker-DTO-object

We hebben een ... nodig Object voor gegevensoverdracht om alle registratie-informatie naar onze Spring-backend te sturen. De DTO object moet alle informatie hebben die we later nodig hebben wanneer we onze Gebruiker voorwerp:

openbare klasse UserDto {@NotNull @NotEmpty private String firstName; @NotNull @NotEmpty private String lastName; @NotNull @NotEmpty privé String-wachtwoord; private String matchingPassword; @NotNull @NotEmpty privé String-e-mail; // standaard getters en setters}

Merk op dat we standaard hebben gebruikt javax.validation annotaties op de velden van het DTO-object. Later gaan we dat ook doen implementeer onze eigen aangepaste validatie-annotaties om het formaat van het e-mailadres te valideren, evenals voor de wachtwoordbevestiging. (zien Sectie 5)

4. De registratiecontroller

EEN Aanmelden link op de Log in pagina brengt de gebruiker naar het registratie bladzijde. Deze back-end voor die pagina leeft in de registratiecontroller en is toegewezen aan "/Gebruikersregistratie":

Voorbeeld 4.1. - De showRegistration Methode

@GetMapping ("/ user / registration") public String showRegistrationForm (WebRequest-verzoek, modelmodel) {UserDto userDto = nieuwe UserDto (); model.addAttribute ("gebruiker", userDto); retourneer "registratie"; }

Wanneer de controller het verzoek ontvangt "/Gebruikersregistratie", het creëert het nieuwe UserDto object dat het registratie vorm, bindt het en keert terug - vrij eenvoudig.

5. Validatie van registratiegegevens

Vervolgens - laten we eens kijken naar de validaties die de controller zal uitvoeren bij het registreren van een nieuw account:

  1. Alle verplichte velden zijn ingevuld (geen lege of lege velden)
  2. Het e-mailadres is geldig (correct)
  3. Het wachtwoordbevestigingsveld komt overeen met het wachtwoordveld
  4. Het account bestaat nog niet

5.1. De ingebouwde validatie

Voor de eenvoudige controles gebruiken we de standaardvalidatie-annotaties op het DTO-object - annotaties zoals @Niet nul, @Niet leeg, enz.

Om het validatieproces te activeren, zullen we het object in de controllerlaag eenvoudig annoteren met de @Geldig annotatie:

openbaar ModelAndView registerUserAccount (@ModelAttribute ("user") @Valid UserDto userDto, HttpServletRequest-verzoek, foutenfouten) {...}

5.2. Aangepaste validatie om de geldigheid van e-mail te controleren

Vervolgens - laten we het e-mailadres valideren en ervoor zorgen dat het correct is gevormd. We gaan een aangepaste validator daarvoor, evenals een aangepaste validatie-annotatie - laten we dat noemen @Geldige email.

Een korte kanttekening hier: we rollen onze eigen aangepaste annotatie in plaats van Hibernate's@E-mail omdat Hibernate de oude indeling van intranetadressen beschouwt: [e-mail beveiligd] als geldig (zie Stackoverflow-artikel), wat niet goed is.

Hier is de annotatie van de e-mailvalidatie en de aangepaste validator:

Voorbeeld 5.2.1. - De aangepaste annotatie voor e-mailvalidatie

@Target ({TYPE, FIELD, ANNOTATION_TYPE}) @Retention (RUNTIME) @Constraint (validatedBy = EmailValidator.class) @Documented publiek @interface ValidEmail {String bericht () standaard "Ongeldige e-mail"; Klasse [] groepen () standaard {}; Klasse [] payload () standaard {}; }

Merk op dat we de annotatie bij de VELD niveau - aangezien dat is waar het conceptueel van toepassing is.

Voorbeeld 5.2.2. - De gewoonte E-mailadres Validator:

openbare klasse EmailValidator implementeert ConstraintValidator {privé Pattern patroon; privé Matcher-matcher; private static final String EMAIL_PATTERN = "^ [_ A-Za-z0-9 - +] + (. [_ A-Za-z0-9 -] +) * @" + "[A-Za-z0-9 -] + (. [A-Za-z0-9] +) * (. [A-Za-z] {2,}) $ "; @Override public void initialize (ValidEmail constraintAnnotation) {} @Override public boolean isValid (String e-mail, ConstraintValidatorContext context) {return (validateEmail (e-mail)); } private boolean validateEmail (String e-mail) {pattern = Pattern.compile (EMAIL_PATTERN); matcher = pattern.matcher (e-mail); retourneer matcher.matches (); }}

Laten we nu gebruik de nieuwe annotatie op onze UserDto implementatie:

@ValidEmail @NotNull @NotEmpty privé String-e-mail;

5.3. Aangepaste validatie gebruiken voor wachtwoordbevestiging

We hebben ook een aangepaste annotatie en validator nodig om ervoor te zorgen dat het wachtwoord en matchingPassword velden komen overeen:

Voorbeeld 5.3.1. - De aangepaste annotatie voor het valideren van wachtwoordbevestiging

@Target ({TYPE, ANNOTATION_TYPE}) @Retention (RUNTIME) @Constraint (validatedBy = PasswordMatchesValidator.class) @Documented public @interface PasswordMatches {String message () standaard "Wachtwoorden komen niet overeen"; Klasse [] groepen () standaard {}; Klasse [] payload () standaard {}; }

Merk op dat de @Doelwit annotatie geeft aan dat dit een TYPE niveau annotatie. Dit komt omdat we het geheel nodig hebben UserDto bezwaar maken om de validatie uit te voeren.

De aangepaste validator die door deze annotatie wordt aangeroepen, wordt hieronder weergegeven:

Voorbeeld 5.3.2. De PasswordMatchesValidator Aangepaste validator

openbare klasse PasswordMatchesValidator implementeert ConstraintValidator {@Override public void initialize (PasswordMatches constraintAnnotation) {} @Override public boolean isValid (Object obj, ConstraintValidatorContext context) {UserDto user = (UserDto) obj; retourneer user.getPassword (). is gelijk aan (user.getMatchingPassword ()); }}

Nu de @PasswordMatches annotatie moet worden toegepast op onze UserDto voorwerp:

@PasswordMatches openbare klasse UserDto {...}

Alle aangepaste validaties worden uiteraard samen met alle standaardannotaties geëvalueerd wanneer het hele validatieproces wordt uitgevoerd.

5.4. Controleer of het account nog niet bestaat

De vierde controle die we zullen implementeren, is controleren of het e-mail account bestaat nog niet in de database.

Dit wordt uitgevoerd nadat het formulier is gevalideerd en met behulp van het Gebruikersservice implementatie.

Voorbeeld 5.4.1. - De controller createUserAccount Methode Roept het Gebruikersservice Object

@PostMapping ("/ user / registration") openbaar ModelAndView registerUserAccount (@ModelAttribute ("user") @Valid UserDto userDto, HttpServletRequest-verzoek, foutenfouten) {probeer {User registered = userService.registerNewUserAccount (userDto); } catch (UserAlreadyExistException uaeEx) {mav.addObject ("message", "Er bestaat al een account voor die gebruikersnaam / e-mail."); terugkeer mav; } // rest van de implementatie} 

Voorbeeld 5.4.2. - GebruikerOnderhoud Controleert op dubbele e-mails

@Service public class UserService implementeert IUserService {@Autowired private UserRepository repository; @Transactional @Override public User registerNewUserAccount (UserDto userDto) gooit UserAlreadyExistException {if (emailExist (userDto.getEmail ())) {throw nieuwe UserAlreadyExistException ("Er is een account met dat e-mailadres:" + userDto.getEmail ()); } ... // de rest van de registratiebewerking} private boolean emailExist (String e-mail) {return userRepository.findByEmail (e-mail)! = null; }}

De UserService vertrouwt op het UserRepository class om te controleren of een gebruiker met een bepaald e-mailadres al in de database bestaat.

Nu - de daadwerkelijke implementatie van het UserRepository in de persistentielaag is niet relevant voor het huidige artikel. Een snelle manier is natuurlijk om Spring Data te gebruiken om de repository-laag te genereren.

6. Aanhoudende gegevens en afronden van formulierverwerking

Ten slotte - laten we de registratielogica implementeren in onze controllerlaag:

Voorbeeld 6.1.1. - De Account Registreren Methode in de controller

@PostMapping ("/ user / registration") openbaar ModelAndView registerUserAccount (@ModelAttribute ("user") @Valid UserDto userDto, HttpServletRequest-verzoek, foutenfouten) {probeer {User registered = userService.registerNewUserAccount (userDto); } catch (UserAlreadyExistException uaeEx) {mav.addObject ("message", "Er bestaat al een account voor die gebruikersnaam / e-mail."); terugkeer mav; } retourneer nieuw ModelAndView ("successRegister", "user", userDto); } 

Dingen om op te merken in de bovenstaande code:

  1. De controller retourneert een ModelAndView object wat de handige klasse is voor het verzenden van modelgegevens (gebruiker) gekoppeld aan het uitzicht.
  2. De controller zal doorverwijzen naar het registratieformulier als er fouten zijn ingesteld op het moment van validatie.

7.De Gebruikersservice - Registreer operatie

Laten we de implementatie van de registratiebewerking in het Gebruikersservice:

Voorbeeld 7.1. De IUserService Koppel

openbare interface IUserService {User registerNewUserAccount (UserDto userDto) gooit UserAlreadyExistException; }

Voorbeeld 7.2. - De Gebruikersservice Klasse

@Service public class UserService implementeert IUserService {@Autowired private UserRepository repository; @Transactional @Override public User registerNewUserAccount (UserDto userDto) gooit UserAlreadyExistException {if (emailExists (userDto.getEmail ())) {gooi nieuwe UserAlreadyExistException ("Er is een account met dat e-mailadres: + userDto.getEmail ());} Gebruiker user = nieuwe gebruiker (); user.setFirstName (userDto.getFirstName ()); user.setLastName (userDto.getLastName ()); user.setPassword (userDto.getPassword ()); user.setEmail (userDto.getEmail ()) ; user.setRoles (Arrays.asList ("ROLE_USER")); return repository.save (gebruiker);} private boolean emailExists (String e-mail) {return userRepository.findByEmail (e-mail)! = null;}}

8. Gebruikersgegevens laden voor beveiligingsaanmelding

In ons vorige artikel was het inloggen met behulp van hard gecodeerde inloggegevens. Laten we dat veranderen en gebruik de nieuw geregistreerde gebruikersinformatie en referenties. We implementeren een aangepaste UserDetailsService om de inloggegevens voor inloggen te controleren vanuit de persistentielaag.

8.1. De gewoonte UserDetailsService

Laten we beginnen met de implementatie van de service voor aangepaste gebruikersgegevens:

@Service @Transactional openbare klasse MyUserDetailsService implementeert UserDetailsService {@Autowired privé UserRepository userRepository; // openbare UserDetails loadUserByUsername (String e-mail) genereert UsernameNotFoundException {User user = userRepository.findByEmail (e-mail); if (user == null) {throw new UsernameNotFoundException ("Geen gebruiker gevonden met gebruikersnaam:" + e-mail); } boolean enabled = true; boolean accountNonExpired = true; boolean credentialsNonExpired = true; boolean accountNonLocked = true; retourneer nieuwe org.springframework.security.core.userdetails.User (user.getEmail (), user.getPassword (). toLowerCase (), enabled, accountNonExpired, credentialsNonExpired, accountNonLocked, getAuthorities (user.getRoles ())); } privé statische lijst getAuthorities (lijstrollen) {lijstautoriteiten = nieuwe ArrayList (); voor (String rol: rollen) {autoriteiten.add (nieuwe SimpleGrantedAuthority (rol)); } terugkeerautoriteiten; }}

8.2. Schakel de nieuwe authenticatieleverancier in

Om de nieuwe gebruikersservice in de Spring Security-configuratie in te schakelen, hoeven we alleen maar een verwijzing naar het UserDetailsService binnen in de authenticatie-manager element en voeg het UserDetailsService Boon:

Voorbeeld 8.2. - De Authentication Manager en het UserDetailsService

Of, via Java-configuratie:

@Autowired privé MyUserDetailsService userDetailsService; @Override protected void configure (AuthenticationManagerBuilder auth) genereert Uitzondering {auth.userDetailsService (userDetailsService); }

9. Conclusie

En we zijn klaar - een compleet en bijna productie klaar registratieproces geïmplementeerd met Spring Security en Spring MVC. Vervolgens gaan we het proces bespreken van het activeren van het nieuw geregistreerde account door het e-mailadres van de nieuwe gebruiker te verifiëren.

De implementatie van deze Spring Security REST-zelfstudie is te vinden in het GitHub-project - dit is een op Eclipse gebaseerd project, dus het moet gemakkelijk te importeren en uit te voeren zijn zoals het is.

De volgende » Registratie - Activeer een nieuw account via e-mail « Vorige zelfstudie over beveiligingsregistratie in het voorjaar

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