Рубрики
Без рубрики

Процесс Регистрации С Spring Security

Реализуйте полную регистрацию с помощью Spring Security и Spring MVC.

Автор оригинала: baeldung.

1. Обзор

В этой статье мы реализуем базовый процесс регистрации с помощью Spring Security. Это строится на основе концепций , рассмотренных в предыдущей статье, где мы рассматривали логин.

Цель здесь состоит в том, чтобы добавить полный процесс регистрации , который позволяет пользователю регистрироваться, проверять и сохранять пользовательские данные.

Дальнейшее чтение:

Сервлет 3 Асинхронная поддержка с Spring MVC и Spring Security

Пружинная защита с Тимелеафом

Spring Security – Заголовки управления кэшем

2. Страница Регистрации

Во – первых-давайте реализуем простую страницу регистрации, отображающую следующие поля :

  • имя (имя и фамилия)
  • электронная почта
  • пароль (и поле подтверждения пароля)

В следующем примере показан простой registration.html страница:

Пример 2.1.



form

Validation error

Validation error

Validation error

Validation error

login

3. Пользователь Возражает

Нам нужен объект передачи данных для отправки всей регистрационной информации в наш бэкэнд Spring. Объект DTO должен иметь всю информацию, которая нам потребуется позже, когда мы создадим и заполним наш объект User :

public class UserDto {
    @NotNull
    @NotEmpty
    private String firstName;
    
    @NotNull
    @NotEmpty
    private String lastName;
    
    @NotNull
    @NotEmpty
    private String password;
    private String matchingPassword;
    
    @NotNull
    @NotEmpty
    private String email;
    
    // standard getters and setters
}

Обратите внимание, что мы использовали стандартные javax.validation аннотации к полям объекта DTO. Позже мы также реализуем наши собственные пользовательские аннотации проверки для проверки формата адреса электронной почты, а также для подтверждения пароля. (см. Раздел 5)

4. Регистрационный Контролер

Ссылка Регистрация на странице вход приведет пользователя на страницу регистрация . Этот бэкэнд для этой страницы живет в контроллере регистрации и сопоставляется с “/user/registration” :

Пример 4.1. показать Регистрацию Метод

@GetMapping("/user/registration")
public String showRegistrationForm(WebRequest request, Model model) {
    UserDto userDto = new UserDto();
    model.addAttribute("user", userDto);
    return "registration";
}

Когда контроллер получает запрос “/user/registration” , он создает новый объект UserDTO , который будет поддерживать форму registration , связывает ее и возвращает – довольно просто.

5. Проверка Регистрационных Данных

Далее – давайте посмотрим на валидации, которые контроллер будет выполнять при регистрации новой учетной записи:

  1. Все обязательные поля заполнены (нет пустых или нулевых полей)
  2. Адрес электронной почты действителен (хорошо сформирован)
  3. Поле подтверждения пароля совпадает с полем пароля
  4. Учетная запись еще не существует

5.1. Встроенная Валидация

Для простых проверок мы будем использовать готовые аннотации проверки бобов на объекте DTO – аннотации типа @NotNull , @NotEmpty и т. Д.

Чтобы запустить процесс проверки, мы просто аннотируем объект в слое контроллера аннотацией @Valid :

public ModelAndView registerUserAccount(
  @ModelAttribute("user") @Valid UserDto userDto, 
  HttpServletRequest request, Errors errors) {
    ...
}

5.2. Пользовательская валидация для проверки валидности электронной почты

Далее – давайте проверим адрес электронной почты и убедимся, что он правильно сформирован. Для этого мы создадим пользовательский валидатор , а также пользовательскую аннотацию валидации – назовем это @ValidEmail .

Краткое примечание здесь – мы сворачиваем нашу собственную пользовательскую аннотацию вместо Hibernate | @Email , потому что Hibernate считает старый формат адресов интрасети: [email protected] допустимым (см. Stackoverflow article), что не очень хорошо.

Вот аннотация проверки электронной почты и пользовательский валидатор:

Пример 5.2.1. – Пользовательская аннотация для проверки электронной почты

@Target({TYPE, FIELD, ANNOTATION_TYPE}) 
@Retention(RUNTIME)
@Constraint(validatedBy = EmailValidator.class)
@Documented
public @interface ValidEmail {   
    String message() default "Invalid email";
    Class[] groups() default {}; 
    Class[] payload() default {};
}

Обратите внимание, что мы определили аннотацию на уровне FIELD – поскольку именно там она применяется концептуально.

Пример 5.2.2. – Пользовательский EmailValidato r:

public class EmailValidator 
  implements ConstraintValidator {
    
    private Pattern pattern;
    private 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 email, ConstraintValidatorContext context){   
        return (validateEmail(email));
    } 
    private boolean validateEmail(String email) {
        pattern = Pattern.compile(EMAIL_PATTERN);
        matcher = pattern.matcher(email);
        return matcher.matches();
    }
}

Давайте теперь использовать новую аннотацию в нашей UserDTO реализации:

@ValidEmail
@NotNull
@NotEmpty
private String email;

5.3. Использование пользовательской проверки для подтверждения пароля

Нам также нужна пользовательская аннотация и валидатор, чтобы убедиться, что поля password и matching Password совпадают:

Пример 5.3.1. – Пользовательская аннотация для проверки подтверждения пароля

@Target({TYPE,ANNOTATION_TYPE}) 
@Retention(RUNTIME)
@Constraint(validatedBy = PasswordMatchesValidator.class)
@Documented
public @interface PasswordMatches { 
    String message() default "Passwords don't match";
    Class[] groups() default {}; 
    Class[] payload() default {};
}

Обратите внимание, что аннотация @Target указывает на то, что это аннотация уровня TYPE . Это происходит потому, что для выполнения проверки нам нужен весь объект UserDTO .

Пользовательский валидатор, который будет вызван этой аннотацией, показан ниже:

Пример 5.3.2. Валидатор совпадений паролей Пользовательский валидатор

public class PasswordMatchesValidator 
  implements ConstraintValidator { 
    
    @Override
    public void initialize(PasswordMatches constraintAnnotation) {       
    }
    @Override
    public boolean isValid(Object obj, ConstraintValidatorContext context){   
        UserDto user = (UserDto) obj;
        return user.getPassword().equals(user.getMatchingPassword());    
    }     
}

Теперь аннотацию @PasswordMatches следует применить к нашему объекту UserDTO :

@PasswordMatches
public class UserDto {
   ...
}

Все пользовательские проверки, конечно, оцениваются вместе со всеми стандартными аннотациями, когда выполняется весь процесс проверки.

5.4. Убедитесь, Что Учетная Запись Еще Не Существует

Четвертая проверка, которую мы осуществим, – это проверка того, что учетная запись email еще не существует в базе данных.

Это выполняется после того, как форма была проверена, и это делается с помощью реализации User Service .

Пример 5.4.1. – Контроллер создать Учетную запись Пользователя Метод t Вызывает UserService O объект

@PostMapping("/user/registration")
public ModelAndView registerUserAccount
      (@ModelAttribute("user") @Valid UserDto userDto, 
      HttpServletRequest request, Errors errors) {
    
    try {
        User registered = userService.registerNewUserAccount(userDto);
    } catch (UserAlreadyExistException uaeEx) {
        mav.addObject("message", "An account for that username/email already exists.");
        return mav;
    }

    // rest of the implementation
}

Пример 5.4.2. – Пользователь Услуга Проверяет наличие дубликатов писем

@Service
public class UserService implements IUserService {
    @Autowired
    private UserRepository repository; 
    
    @Transactional
    @Override
    public User registerNewUserAccount(UserDto userDto) 
      throws UserAlreadyExistException {
        
        if (emailExist(userDto.getEmail())) {  
            throw new UserAlreadyExistException(
              "There is an account with that email address: "
              +  userDto.getEmail());
        }
        ...
        // the rest of the registration operation
    }
    private boolean emailExist(String email) {
        return userRepository.findByEmail(email) != null;
    }
}

Служба пользователей использует класс UserRepository для проверки того, существует ли пользователь с заданным адресом электронной почты в базе данных.

Теперь – фактическая реализация UserRepository в слое персистентности не имеет отношения к текущей статье. Один из быстрых способов-это, конечно, использовать данные Spring для создания слоя репозитория .

6. Сохранение данных и Окончательная обработка форм

Наконец – давайте реализуем логику регистрации в нашем слое контроллера:

Пример 6.1.1. Регистрация Учетной записи Метод в контроллере

@PostMapping("/user/registration")
public ModelAndView registerUserAccount(
  @ModelAttribute("user") @Valid UserDto userDto, 
  HttpServletRequest request, Errors errors) { 
    
    try {
        User registered = userService.registerNewUserAccount(userDto);
    } catch (UserAlreadyExistException uaeEx) {
        mav.addObject("message", "An account for that username/email already exists.");
        return mav;
    }

    return new ModelAndView("successRegister", "user", userDto);
}

Что нужно заметить в приведенном выше коде:

  1. Контроллер возвращает объект ModelAndView , который является удобным классом для отправки данных модели ( user ), привязанных к представлению.
  2. Контроллер перенаправит вас на регистрационную форму, если во время проверки будут установлены какие-либо ошибки.

7. Операция UserService – Register

Завершим реализацию операции регистрации в Пользовательском сервисе :

Пример 7.1. IUserService Интерфейс

public interface IUserService {
    User registerNewUserAccount(UserDto userDto)     
      throws UserAlreadyExistException;
}

Пример 7.2. UserService Класс

@Service
public class UserService implements IUserService {
    @Autowired
    private UserRepository repository;
    
    @Transactional
    @Override
    public User registerNewUserAccount(UserDto userDto) 
      throws UserAlreadyExistException {
        
        if (emailExists(userDto.getEmail())) {   
            throw new UserAlreadyExistException(
              "There is an account with that email address:  
              + userDto.getEmail());
        }
        User user = new User();    
        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(user);       
    }

    private boolean emailExists(String email) {
        return userRepository.findByEmail(email) != null;
    }
}

8. Загрузка данных пользователя для входа в систему безопасности

В нашей предыдущей статье вход в систему осуществлялся с использованием жестко закодированных учетных данных. Давайте изменим это и используем только что зарегистрированную информацию пользователя и учетные данные. Мы реализуем пользовательский UserDetailsService для проверки учетных данных для входа в систему с уровня персистентности.

8.1. Пользовательский сервис UserDetailsService

Давайте начнем с пользовательской реализации userdetailsservice:

@Service
@Transactional
public class MyUserDetailsService implements UserDetailsService {
 
    @Autowired
    private UserRepository userRepository;
    // 
    public UserDetails loadUserByUsername(String email)
      throws UsernameNotFoundException {
 
        User user = userRepository.findByEmail(email);
        if (user == null) {
            throw new UsernameNotFoundException(
              "No user found with username: "+ email);
        }
        boolean enabled = true;
        boolean accountNonExpired = true;
        boolean credentialsNonExpired = true;
        boolean accountNonLocked = true;
        return  new org.springframework.security.core.userdetails.User
          (user.getEmail(), 
          user.getPassword().toLowerCase(), enabled, accountNonExpired, 
          credentialsNonExpired, accountNonLocked, 
          getAuthorities(user.getRoles()));
    }
    
    private static List getAuthorities (List roles) {
        List authorities = new ArrayList<>();
        for (String role : roles) {
            authorities.add(new SimpleGrantedAuthority(role));
        }
        return authorities;
    }
}

8.2. Включите Новый поставщик аутентификации

Чтобы включить новую пользовательскую службу в конфигурации Spring Security, нам просто нужно добавить ссылку на UserDetailsService внутри элемента authentication-manager и добавить UserDetailsService bean:

Пример 8.2.- Менеджер аутентификации и UserDetailsService


     

 

Или через конфигурацию Java:

@Autowired
private MyUserDetailsService userDetailsService;

@Override
protected void configure(AuthenticationManagerBuilder auth) 
  throws Exception {
    auth.userDetailsService(userDetailsService);
}

9. Заключение

И мы закончили – полный и почти готовый к производству процесс регистрации реализован с помощью Spring Security и Spring MVC. Далее мы обсудим процесс активации только что зарегистрированной учетной записи путем проверки электронной почты нового пользователя.

Реализацию этого учебника Spring Security REST можно найти в проекте GitHub – это проект на основе Eclipse, поэтому его должно быть легко импортировать и запускать как есть.