0. Прочитав эту статью, вы узнаете
- Как реализовать базовый процесс проверки регистрации
- Как настроить аннотацию
1 Обзор
В этой статье мы будем использовать Spring Boot для реализации простого процесса регистрации и проверки учетной записи электронной почты.
Наша цель — добавить полный процесс регистрации, который позволит пользователям регистрироваться, аутентифицировать и сохранять пользовательские данные.
2. Создайте пользовательский объект DTO
Во-первых, нам нужен DTO для хранения регистрационной информации пользователя. Этот объект должен содержать основную информацию, необходимую нам при регистрации и аутентификации.
Пример 2.1 Определение UserDto
package com.savagegarden.web.dto;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
public class UserDto {
@NotBlank
private String username;
@NotBlank
private String password;
@NotBlank
private String repeatedPassword;
@NotBlank
private String email;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getRepeatedPassword() {
return repeatedPassword;
}
public void setRepeatedPassword(String repeatedPassword) {
this.repeatedPassword = repeatedPassword;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
}
Обратите внимание, что мы используем стандартные поля объекта DTO.javax.validation
аннотация--@NotBlank
.
@NotBlank
,@NotEmpty
,@NotNull
разница
@NotNull:
Применяется к объектам CharSequence, Collection, Map и Array, не может быть нулевым, но может быть пустым набором (размер = 0).
@NotEmpty:
Применяется к объектам CharSequence, Collection, Map и Array, не может быть нулевым, а размер связанного объекта больше 0.
@NotBlank:
Эту аннотацию можно использовать только для типов String. Строка не является нулевой, а усеченная длина больше 0 после удаления символов пробела на обоих концах.
В следующих разделах мы также настроим аннотации для проверки формата адреса электронной почты и подтверждения дополнительных паролей.
3. Внедрить зарегистрированный контроллер
Ссылка для регистрации на странице входа ведет пользователя на страницу регистрации:
Пример 3.1 Определение RegistrationController
package com.savagegarden.web.controller;
import com.savagegarden.web.dto.UserDto;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
@Controller
public class RegistrationController {
@GetMapping("/user/registration")
public String showRegistrationForm(Model model) {
model.addAttribute("user", new UserDto());
return "registration";
}
}
когдаRegistrationController
получил запрос/user/registration
, он создает новый объект UserDto, привязывает его к модели и возвращает страницу регистрации.registration.html
.
Объект модели отвечает за передачу данных между контроллером и представлением, которое представляет данные.
На самом деле данные, помещенные в атрибуты модели, будут скопированы в атрибуты ответа сервлета, чтобы представление могло найти их там.
В широком смысле под Моделью понимается M в среде MVC, то есть Модель. В узком смысле Model — это коллекция ключей и значений.
4. Проверьте регистрационные данные
Далее рассмотрим валидацию, которую будет выполнять контроллер при регистрации новой учетной записи:
- Все обязательные поля заполнены, пустых полей нет.
- Адрес электронной почты действителен
- Поле подтверждения пароля совпадает с полем пароля
- Аккаунт не существует
4.1 Встроенная аутентификация
Для простых проверок мы будем использовать @NotBlank для проверки объектов DTO.
Чтобы запустить процесс проверки, мы проверим объект с помощью аннотации @Valid в контроллере.
Пример 4.1. Регистрация учетной записи пользователя
public ModelAndView registerUserAccount(@ModelAttribute("user") @Valid UserDto userDto,
HttpServletRequest request, Errors errors) {
//...
}
4.2 Пользовательская проверка для проверки действительности электронной почты
Затем давайте проверим адрес электронной почты, чтобы убедиться, что он имеет правильный формат. Для этого мы создадим собственный валидатор вместе с пользовательской аннотацией проверки --IsEmailValid
.
Ниже приведены примечания для проверки электронной почты.IsEmailValid
и собственный валидаторEmailValidator
:
Почему бы не использовать встроенный в Hibernate
Потому что в спящем режиме
XXX@XXX
Электронные письма, подобные этому, на самом деле незаконны.Заинтересованные читатели и друзья могут переехать сюдаHibernate validator: @Email accepts ask@stackoverflow as valid?.
Пример 4.2.1 Определение аннотации IsEmailVaild
package com.savagegarden.validation;
import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import javax.validation.Constraint;
import javax.validation.Payload;
@Target({ TYPE, FIELD, ANNOTATION_TYPE })
@Retention(RUNTIME)
@Constraint(validatedBy = EmailValidator.class)
@Documented
public @interface IsEmailVaild {
String message() default "Invalid Email";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
@Target
Роль состоит в том, чтобы описать область действия объекта, измененного аннотацией.
@Retention
Роль состоит в том, чтобы указать, как долго сохраняются аннотации, аннотированные им.
@Constraint
Роль заключается в описании метода пользовательской аннотации.
@Documented
Роль состоит в том, чтобы указать, что аннотация, измененная этой аннотацией, может быть документирована такими инструментами, как javadoc.о том, как настроить один
Java Annotation
, а заинтересованные друзья могут взглянуть на мою другую статью.
Пример 4.2.2 Определение EmailValidator
package com.savagegarden.validation;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
public class EmailValidator implements ConstraintValidator<IsEmailVaild, String> {
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,})$";
private static final Pattern PATTERN = Pattern.compile(EMAIL_PATTERN);
@Override
public void initialize(IsEmailVaild constraintAnnotation) {
}
@Override
public boolean isValid(final String username, final ConstraintValidatorContext context) {
return (validateEmail(username));
}
private boolean validateEmail(final String email) {
Matcher matcher = PATTERN.matcher(email);
return matcher.matches();
}
}
Теперь давайте воспользуемся новой аннотацией в нашей реализации UserDto.
@NotBlank
@IsEmailVaild
private String email;
4.3 Используйте пользовательскую аутентификацию для подтверждения пароля
Нам также нужна пользовательская аннотация и валидатор, чтобы гарантироватьUserDto
серединаpassword
иrepeatedPassword
поля совпадают.
Пример 4.3.1 Определение аннотации IsPasswordMatching
package com.savagegarden.validation;
import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import javax.validation.Constraint;
import javax.validation.Payload;
@Target({ TYPE, ANNOTATION_TYPE })
@Retention(RUNTIME)
@Constraint(validatedBy = PasswordMatchingValidator.class)
@Documented
public @interface IsPasswordMatching {
String message() default "Passwords don't match";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
Пожалуйста, обрати внимание,@Target
Аннотация указывает, что это аннотация уровня типа. Это потому, что нам нужен весь объект UserDto для выполнения проверки.
Пример 4.3.2 Определение PasswordMatchingValidator
package com.savagegarden.validation;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import com.savagegarden.web.dto.UserDto;
public class PasswordMatchingValidator implements ConstraintValidator<IsPasswordMatching, Object> {
@Override
public void initialize(final IsPasswordMatching constraintAnnotation) {
//
}
@Override
public boolean isValid(final Object obj, final ConstraintValidatorContext context) {
final UserDto user = (UserDto) obj;
return user.getPassword().equals(user.getRepeatedPassword());
}
}
Теперь будет@IsPasswordMatching
Аннотация применяется к нашему объекту UserDto.
@IsPasswordMatching
public class UserDto {
//...
}
4.4 Проверьте, существует ли уже учетная запись
Четвертая проверка, которую мы хотим реализовать, — убедиться, что учетная запись электронной почты уже существует в базе данных.
Это делается после проверки формы, и мы помещаем эту проверку в UserService.
Пример 4.4.1 Пользовательская служба
package com.savagegarden.service.impl;
import com.savagegarden.error.user.UserExistException;
import com.savagegarden.persistence.dao.UserRepository;
import com.savagegarden.persistence.model.User;
import com.savagegarden.service.IUserService;
import com.savagegarden.web.dto.UserDto;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import javax.transaction.Transactional;
@Service
@Transactional
public class UserService implements IUserService {
@Autowired
private UserRepository userRepository;
@Autowired
private PasswordEncoder passwordEncoder;
@Override
public User registerNewUserAccount(UserDto userDto) throws UserExistException {
if (hasEmailExisted(userDto.getEmail())) {
throw new UserExistException("The email has already existed: "
+ userDto.getEmail());
}
User user = new User();
user.setUsername(userDto.getUsername());
user.setPassword(passwordEncoder.encode(userDto.getPassword()));
user.setEmail(userDto.getEmail());
return userRepository.save(user);
}
private boolean hasEmailExisted(String email) {
return userRepository.findByEmail(email) != null;
}
}
использовать
@Transactional
Включите аннотацию транзакции, почему@Transactional
Добавлено на уровне службы вместо уровня DAO?Если наша аннотация транзакции
@Transactional
Если он добавляется на уровень DAO, пока выполняются добавление, удаление и изменение, транзакция должна быть отправлена, поэтому характеристики транзакции не могут быть задействованы, особенно согласованность транзакции. Когда возникает проблема параллелизма, данные, которые пользователь находит в базе данных, будут необъективными.В общем, наш сервисный уровень может вызывать несколько уровней DAO, нам нужно только добавить аннотацию транзакции к сервисному уровню.
@Transactional
, так что мы можем обрабатывать несколько запросов в одной транзакции, и характеристики транзакции будут полностью задействованы.
UserService полагается на класс UserRepository, чтобы проверить, существует ли учетная запись пользователя с таким же почтовым ящиком в базе данных. Разумеется, в этой статье мы не будем касаться реализации UserRepository.
5. Постоянная обработка
Затем приступаем к реализацииRegistrationController
логика сохранения в .
@PostMapping("/user/registration")
public ModelAndView registerUserAccount(
@ModelAttribute("user") @Valid UserDto userDto,
HttpServletRequest request,
Errors errors) {
try {
User registered = userService.registerNewUserAccount(userDto);
} catch (UserExistException uaeEx) {
ModelAndView mav = new ModelAndView();
mav.addObject("message", "An account for that username/email already exists.");
return mav;
}
return new ModelAndView("successRegister", "user", userDto);
}
В приведенном выше коде мы можем найти:
- Мы создали объект ModelAndView, который может хранить данные и возвращать представление.
Три распространенных варианта использования ModelAndView
(1) new ModelAndView(String viewName, String attributeName, Object attributeValue);
(2) mav.setViewName(String viewName);
mav.addObejct(String attributeName, Object attributeValue);
(3) new ModelAndView(String viewName);
- Если в процессе регистрации произойдет какая-либо ошибка, он вернется на страницу регистрации.
6. Безопасный вход
В этом разделе мы реализуем пользовательскийUserDetailsService
, который проверяет учетные данные для входа на уровне сохраняемости.
6.1 Настройка службы сведений о пользователе
Начнем с обычаяUserDetailsService
Начинать.
Пример 6.1.1 MyUserDetailsService
@Service
@Transactional
public class MyUserDetailsService implements UserDetailsService {
@Autowired
private UserRepository userRepository;
@Override
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<GrantedAuthority> getAuthorities (List<String> roles) {
List<GrantedAuthority> authorities = new ArrayList<>();
for (String role : roles) {
authorities.add(new SimpleGrantedAuthority(role));
}
return authorities;
}
}
6.2 Включить нового поставщика аутентификации
Затем, для того, чтобы действительно иметь возможность включить пользовательскийMyUserDetailsService
, нам тоже нужноSecurityConfig
Добавьте следующий код в файл конфигурации:
@Override
protected void configure(final AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(authProvider());
}
Из-за ограниченности места мы не будем здесь подробно останавливатьсяSecurityConfig
конфигурационный файл.
7. Заключение
На данный момент мы завершили базовый процесс регистрации пользователя, реализованный Spring Boot. Страницы и некоторые классы в проекте не отражены в статье.Друзья кому нужно могут подписаться на мой официальный аккаунт花园野人
,Ответитьzhuce
Получите код проекта.