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

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

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

Обзор

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

Распространенной и эффективной стратегией для этого является отправка ссылки с подтверждением на электронную почту пользователя после регистрации. Как только пользователь нажимает на уникальную ссылку, его учетная запись активируется, и он может предпринимать дальнейшие действия на веб-сайте.

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

Настройка проекта

Как всегда, проще всего начать с предварительно настроенного Spring Boot проекта с использованием Spring Initializr . Выберите зависимости для Web , Безопасность , Почта , JPA , Thymeleaf и MySQL и создайте проект:

Мы будем использовать Spring Security и Spring MVC для этого проекта. Для уровня данных мы будем использовать Spring Data , поскольку он уже предоставляет нам операции CRUD для данной сущности и динамический вывод запросов из имен методов репозитория.

Кроме того, мы будем использовать Hibernate в качестве поставщика JPA и базы данных MySQL .

Если вам интересно прочитать больше о JPA, мы рассмотрим это здесь: Руководство по JPA Spring Data .

Зависимости

Давайте взглянем на зависимости в pom.xml файл, который импортирует все необходимые библиотеки в соответствии с описанием выше:


    
        org.springframework.boot
        spring-boot-starter-web
    
    
        org.springframework.boot
        spring-boot-starter-security
    
    
        mysql
        mysql-connector-java
        runtime
    
    
        org.springframework.boot
        spring-boot-starter-data-jpa
    
    
        org.springframework.boot
        spring-boot-starter-mail
    
    
            org.springframework.boot
            spring-boot-starter-thymeleaf
        

Теперь, когда проект полностью готов, мы можем приступить к кодированию!

Реализация

Свойства Пружины

Давайте начнем с настройки свойств Spring в application.properties :

server.port = 8082
logging.level.org.springframework = WARN
logging.level.org.hibernate = WARN
logging.level.com.springsecurity.demo = DEBUG

####### Data-Source Properties #######
spring.datasource.url = jdbc:mysql://localhost:3306/demodb?useSSL=false
spring.datasource.username = username
spring.datasource.password = password
spring.datasource.driver-class-name = com.mysql.jdbc.Driver

###### JPA Properties ######
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQL5Dialect
spring.jpa.generate-ddl = true
spring.jpa.show-sql = true

###### Email Properties ######
spring.mail.host = smtp.gmail.com
spring.mail.port = 587
spring.mail.properties.mail.smtp.starttls.enable = true
spring.mail.username = [email protected]
spring.mail.password = examplepassword
spring.mail.properties.mail.smtp.starttls.required = true
spring.mail.properties.mail.smtp.auth = true
spring.mail.properties.mail.smtp.connectiontimeout = 5000
spring.mail.properties.mail.smtp.timeout = 5000
spring.mail.properties.mail.smtp.writetimeout = 5000

Для этого примера мы используем SMTP-сервер Gmail. Я запускаю свой сервер tomcat на порту 8082 .

Убедитесь, что вы предоставили правильные учетные данные MySQL и учетной записи электронной почты в соответствии с вашей системой. С настройкой свойств JPA мы можем начать с нашей бизнес-логики.

Настройка JPA

У нас есть две модели для этого приложения – Пользователь и Токен подтверждения :

@Entity
public class User {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private long userid;

    private String emailId;

    private String password;

    private String firstName;

    private String lastName;

    private boolean isEnabled;

    // getters and setters
}

Простой класс POJO, снабженный стандартными аннотациями Spring.

Теперь давайте перейдем ко второй модели:

@Entity
public class ConfirmationToken {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private long tokenid;

    private String confirmationToken;

    @Temporal(TemporalType.TIMESTAMP)
    private Date createdDate;

    @OneToOne(targetEntity = User.class, fetch = FetchType.EAGER)
    @JoinColumn(nullable = false, name = "user_id")
    private User user;

    public ConfirmationToken(User user) {
        this.user = user;
        createdDate = new Date();
        confirmationToken = UUID.randomUUID().toString();
    }

    // getters and setters
}

Маркер подтверждения имеет отношение “один ко многим” с Пользователем сущностью. Поскольку мы установили jpa.generate-ddl в true , Hibernate создает схему таблицы в соответствии с вышеуказанными сущностями.

Первичные ключи в обеих таблицах имеют значение автоматическое увеличение , потому что мы аннотировали столбцы идентификаторов в обоих классах с помощью @Generated(стратегия.АВТО) .

Вот как выглядит сгенерированная схема в базе данных:

Теперь, когда конфигурация JPA завершена, мы приступим к написанию Уровня доступа к данным. Для этого мы будем использовать Spring Data , поскольку он предоставляет базовые CRUD операции из коробки, которых будет достаточно для этого примера.

Кроме того, при использовании данных Spring освобождается код шаблона, например, получение entitymanager или получение сеансов и т. Д:

@Repository("userRepository")
public interface UserRepository extends CrudRepository {
    User findByEmailIdIgnoreCase(String emailId);
}

Spring Data автоматически предоставляет реализацию для запросов к базам данных на основе атрибута, при условии, что мы следуем спецификациям Java Bean. Например, в нашем POJO мы отправили по электронной почте как свойство компонента, и мы хотим найти Пользователя по этому свойству, независимо от случая.

Аналогично, мы также реализуем репозиторий для Токена подтверждения :

public interface ConfirmationTokenRepository extends CrudRepository {
    ConfirmationToken findByConfirmationToken(String confirmationToken);
}

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

Это обеспечивает слабую связь кода приложения с библиотеками Spring.

Служба электронной почты

Как только пользователь завершит регистрацию, нам нужно отправить электронное письмо на адрес электронной почты пользователя. Мы будем использовать Spring Mail API для достижения этой функциональности.

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

@Service("emailSenderService")
public class EmailSenderService {

    private JavaMailSender javaMailSender;

    @Autowired
    public EmailSenderService(JavaMailSender javaMailSender) {
        this.javaMailSender = javaMailSender;
    }

    @Async
    public void sendEmail(SimpleMailMessage email) {
        javaMailSender.send(email);
    }
}

Git Essentials

Ознакомьтесь с этим практическим руководством по изучению Git, содержащим лучшие практики и принятые в отрасли стандарты. Прекратите гуглить команды Git и на самом деле изучите это!

Мы аннотировали класс с помощью @Service , который является вариантом аннотации @Компонента . Это позволяет Spring Boot обнаружить службу и зарегистрировать ее для использования.

Контроллер и просмотр

Теперь у нас есть все сервисы, готовые для нашего примера, и мы можем приступить к написанию UserAccountController :

@Controller
public class UserAccountController {

    @Autowired
    private UserRepository userRepository;

    @Autowired
    private ConfirmationTokenRepository confirmationTokenRepository;

    @Autowired
    private EmailSenderService emailSenderService;

    public ModelAndView displayRegistration(ModelAndView modelAndView, User user)
    {
        modelAndView.addObject("user", user);
        modelAndView.setViewName("register");
        return modelAndView;
    }

    public ModelAndView registerUser(ModelAndView modelAndView, User user)
    {

        User existingUser = userRepository.findByEmailIdIgnoreCase(user.getEmailId());
        if(existingUser != null)
        {
            modelAndView.addObject("message","This email already exists!");
            modelAndView.setViewName("error");
        }
        else
        {
            userRepository.save(user);

            ConfirmationToken confirmationToken = new ConfirmationToken(user);

            confirmationTokenRepository.save(confirmationToken);

            SimpleMailMessage mailMessage = new SimpleMailMessage();
            mailMessage.setTo(user.getEmailId());
            mailMessage.setSubject("Complete Registration!");
            mailMessage.setFrom("[email protected]");
            mailMessage.setText("To confirm your account, please click here : "
            +"emailId", user.getEmailId());

            modelAndView.setViewName("successfulRegisteration");
        }

        return modelAndView;
    }

    public ModelAndView confirmUserAccount(ModelAndView modelAndView, @RequestParam("token")String confirmationToken)
    {
        ConfirmationToken token = confirmationTokenRepository.findByConfirmationToken(confirmationToken);

        if(token != null)
        {
            User user = userRepository.findByEmailIdIgnoreCase(token.getUser().getEmailId());
            user.setEnabled(true);
            userRepository.save(user);
            modelAndView.setViewName("accountVerified");
        }
        else
        {
            modelAndView.addObject("message","The link is invalid or broken!");
            modelAndView.setViewName("error");
        }

        return modelAndView;
    }
    // getters and setters
}

Давайте взглянем на методы в контроллере, что они делают и какие представления они возвращают.

регистрация дисплея() – Отправная точка для пользователя в нашем приложении. Как только пользователь открывает наше приложение, ему отображается страница регистрации с помощью этого метода.

Мы также добавили объект user в представление. Тег <форма> на странице регистрации также включает этот объект, и мы будем использовать поля в форме для заполнения полей объекта.

Этот объект с заполненной информацией будет сохранен в базе данных.

Вот как это выглядит на странице:


    
        Register
    
    
        

регистрация пользователя() – Принимает данные пользователя, введенные на странице регистрации. Spring MVC автоматически делает пользовательский ввод доступным для нас в методе.

Мы сохраняем данные пользователя в таблице пользователей и создаем случайный токен подтверждения. Токен сохраняется с идентификатором электронной почты пользователя в таблице confirmation_token и отправляется по URL-адресу на электронную почту пользователя для проверки.

Пользователю отображается страница успешной регистрации:


    
        Registration Success
    
    
        

Наконец, как только будет получен доступ к URL-адресу электронной почты, будет вызван метод подтвердить учетную запись пользователя () .

Этот метод проверяет маркер, который не должен быть пустым и должен существовать в базе данных, иначе пользователю будет отображена страница с ошибкой ( error.html) :


    
        Registration Success
    
    
        

Если проблем с проверкой нет, учетная запись, связанная с токеном, проверяется. Пользователю отображается сообщение об успешной активации:


    
        Congratulations!
    
    
        

Congratulations! Your account has been activated and email is verified!

Конфигурация безопасности Spring

Теперь давайте настроим модуль безопасности Spring для защиты нашего веб-приложения. Нам нужно убедиться, что для /регистрации и /подтверждения URL-адресов не требуется аутентификация, поскольку они являются целевыми страницами для нового пользователя:

@Configuration
@EnableWebSecurity
    public class SecurityConfiguration extends WebSecurityConfigurerAdapter {

        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http.authorizeRequests()
                .antMatchers("/register").permitAll()
                .antMatchers("/confirm").permitAll();
        }
}

Наконец, нам понадобится метод main в качестве отправной точки для нашего приложения Spring Boot:

@SpringBootApplication
public class RunApplication {
    public static void main(String[] args) {
        SpringApplication.run(RunApplication.class, args);
    }
}

Аннотация @SpringBootApplication направляет Spring Boot для загрузки всех классов конфигурации и компонентов, а также включает автоматическую настройку. Это одна из замечательных функций Spring Boot, мы можем запустить ее с помощью простого основного метода.

Запуск приложения

Мы начинаем тестирование с выбора и запуска приложения RUN.java . Это запускает встроенный сервер Tomcat на порту 8082 и наше приложение будет развернуто.

Далее, давайте откроем браузер и получим доступ к нашему приложению:

После ввода необходимой информации пользователь отправит:

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

Перейдя по ссылке, учетная запись проверяется с помощью уникального токена, и пользователь перенаправляется на страницу успеха:

Вот как это выглядит в базе данных:

Вывод

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

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

Если вам интересно поиграть с исходным кодом, как всегда, он доступен на GitHub