Как правильно организовать регистрацию и авторизацию пользователей сайта (Java)? — Хабр Q&A

Основная конфигурация securityconfig

Наша конфигурация будет уметь:

Введение

Хотелось бы в данном обзоре обсудить такую тему, как безопасность веб-приложений.

В Java есть несколько технологий, которые обеспечивают безопасность:

Login form in java swing with source code tutorial | login page

import javax.swing.*;

import java.awt.*;

import java.awt.event.ActionEvent;

import java.awt.event.ActionListener;

publicclassLoginFrameextendsJFrameimplementsActionListener{

    Container container=getContentPane();

    JLabel userLabel=newJLabel(“USERNAME”);

    JLabel passwordLabel=newJLabel(“PASSWORD”);

    JTextField userTextField=newJTextField();

    JPasswordField passwordField=newJPasswordField();

    JButton loginButton=newJButton(“LOGIN”);

    JButton resetButton=newJButton(“RESET”);

    JCheckBox showPassword=newJCheckBox(“Show Password”);

    LoginFrame(){

        setLayoutManager();

        setLocationAndSize();

        addComponentsToContainer();

        addActionEvent();

    }

    publicvoidsetLayoutManager(){

        container.setLayout(null);

    }

    publicvoidsetLocationAndSize(){

        userLabel.setBounds(50,150,100,30);

        passwordLabel.setBounds(50,220,100,30);

        userTextField.setBounds(150,150,150,30);

        passwordField.setBounds(150,220,150,30);

        showPassword.setBounds(150,250,150,30);

        loginButton.setBounds(50,300,100,30);

        resetButton.setBounds(200,300,100,30);

    }

    publicvoidaddComponentsToContainer(){

        container.add(userLabel);

        container.add(passwordLabel);

        container.add(userTextField);

        container.add(passwordField);

        container.add(showPassword);

        container.add(loginButton);

        container.add(resetButton);

    }

    publicvoidaddActionEvent(){

        loginButton.addActionListener(this);

        resetButton.addActionListener(this);

        showPassword.addActionListener(this);

    }

    @Override

    publicvoidactionPerformed(ActionEvente){

        //Coding Part of LOGIN button

        if(e.getSource()==loginButton){

            StringuserText;

            StringpwdText;

            userText=userTextField.getText();

            pwdText=passwordField.getText();

            if(userText.equalsIgnoreCase(“mehtab”)&&pwdText.equalsIgnoreCase(“12345”)){

                JOptionPane.showMessageDialog(this,“Login Successful”);

            }else{

                JOptionPane.showMessageDialog(this,“Invalid Username or Password”);

            }

        }

        //Coding Part of RESET button

        if(e.getSource()==resetButton){

            userTextField.setText(“”);

            passwordField.setText(“”);

        }

       //Coding Part of showPassword JCheckBox

        if(e.getSource()==showPassword){

            if(showPassword.isSelected()){

                passwordField.setEchoChar((char)0);

            }else{

                passwordField.setEchoChar(‘*’);

            }

        }

    }

}

publicclassLogin{

    publicstaticvoidmain(String[]a){

        LoginFrame frame=newLoginFrame();

        frame.setTitle(“Login Form”);

        frame.setVisible(true);

        frame.setBounds(10,10,370,600);

        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        frame.setResizable(false);

    }

}

Аутентификация

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

Мы с Вами не зря переключились на Tomcat, т.к. Tomcat имеет хорошо описанную архитектуру (см. “

“). Из описания этой архитектуры видно, что Tomcat как веб-сервер представляет веб-приложение как некоторый контекст, который и называют ”

“. Этот контекст позволяет каждому веб-приложению иметь свои настройки, изолированные от других веб-приложений. Кроме того, веб-приложение может влиять на настройки этого контекста. Гибко и удобно. Для более глубокого понимания рекомендуется к прочтению статья “

” и раздел документации Tomcat ”

“.

Как выше было сказано, наше веб-приложение может влиять на Tomcat Context нашего приложения при помощи файла

/META-INF/context.xml

. И одной из очень важных настроек, на которую мы можем повлиять, является Security Realms.

Security Realms

— это некоторая “область безопасности”. Область, для которой указаны определенные настройки безопасности. Соответственно, используя Security Realm мы применяем настройки безопасности, определённые для этого Realm. Security Realms управляются контейнером, т.е. веб-сервером, а не нашим веб-приложением.

” описывает Realm как набор данных о пользователях и их ролях для выполнения аутентификации. Tomcat предоставляет набор различных реализаций Security Realm’ов, одним из которых является ”

“.

Разобравшись немного с терминологией, давайте опишем Tomcat Context в файле

/META-INF/context.xml

Веб-приложение

Итак, нам понадобится веб-приложение. В его создании нам поможет система автоматической сборки проектов Gradle. Благодаря использования Gradle мы сможем при помощи выполнения небольших команд собирать Java проект в нужном нам формате, создавать автоматически нужную структуру каталогов и многое другое.

Подробнее про Gradle можно прочитать в кратком обзоре: “

” или в официальной документации ”

“.

Нам нужно выполнить инициализацию проекта (Initialization), а для этого в Gradle есть специальный плагин: ”

” (Init – сокращение от Initialization, легко запомнить).

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

gradle init --type java-application

После успешного выполнения у нас появится Java проект.

Откроем теперь на редактирование билд скрипт нашего проекта. Билд скрипт — это файл с названием

build.gradle

, который описывает нюансы сборки (билда) приложения. Отсюда и название такое, билд скрипт. Можно сказать, что это скрипт сборки проекта.

Gradle — это такой универсальный инструмент, базовые возможности которого расширяются благодаря плагинам. Поэтому, в первую очередь обратим внимание на блок “plugins” (плагины):


plugins {
    id 'java'
    id 'application'
}

По умолчанию Gradle, в соответствии с указанным нами ”

--type java-application

“, выставил набор некоторых базовых плагинов (core plugins), то есть тех плагинов, которые входят в поставку самого Gradle. Если перейти на сайте

в раздел “Docs” (то есть документация), то слева в списке тем в разделе “Reference” мы видим раздел ”

“, т.е. раздел с описанием этих самых базовых плагинов. Давайте выберем именно те плагины, которые нам нужны, а не те, которые нам сгенерировал Gradle.

Согласно документации, ”

” обеспечивает базовые операции с Java кодом, такие как компиляция исходного кода. Так же, согласно документации, ”

” обеспечивает нас средствами для работы с “executable JVM application”, т.е. с java приложением, которые можно запустить как самостоятельное приложение (например, консольное приложение или приложение с собственным UI). Получается, что плагин “application” нам не нужен, т.к. нам не нужно самостоятельное приложение, нам нужно веб-приложение. Удалим его. А так же настройку “mainClassName”, которая известна только этому плагину.

Далее, в том же разделе “

“, где была приведена ссылка на документацию по Application Plugin, есть ссылка на Gradle War Plugin.

, как сказано в документации, предоставляет поддержку создания Java веб-приложений в формате war. В формате WAR означает, что вместо JAR архива будет создан WAR архив. Кажется, это то, что нам нужно. Кроме того, как сказано в документации, “The War plugin extends the Java plugin”. То есть мы можем заменить плагин java на плагин war. Следовательно, наш блок плагинов в итоге будет иметь следующий вид:


plugins {
    id 'war'
}

Так же в документации к “Gradle War Plugin” сказано, что плагин использует дополнительный “Project Layout”. Layout с английского переводится как расположение. То есть war plugin по умолчанию рассчитывает на существование некоторого расположение файлов, которые он будет использовать для своих задач. Использовать для хранения файлов веб-приложения он будет следующий каталог:

src/main/webapp

Поведения плагина описано так:

То есть плагин будет учитывать файлы из этого расположения при сборке WAR архива нашего веб-приложения.

Кроме того, в документации Gradle War Plugin’а сказано, что данный каталог будет “root of the archive”. И уже в нём мы можем создать каталог WEB-INF и добавить туда файл web.xml. Что это за файл такой?

web.xml

— это “Deployment Descriptor” или “описатель развёртывания”. Это такой файл, который описывает, как нужно настроить наше веб-приложение для работы. В этом файле указывается, какие запросы будет обрабатывать наше приложение, настройки безопасности и многое другое. По своей сути он чем-то похож на manifest файл из JAR файла (см. “

“). Manifest файл рассказывает, как работать с Java Application (т.е. с JAR архивом), а web.xml рассказывает, как работать с Java Web Application (т.е. с WAR архивом).

Само понятие “Deployment Descriptor” возникло не само по себе, а описано в документе “

“.

Любое Java веб-приложение зависит от этого “Servlet API”. Важно понимать, что это API — то есть это описание некоторого контракта взаимодействия. Веб-приложения — это не самостоятельные приложения. Они запускаются на веб-сервере, который обеспечивает сетевое взаимодействие с пользователями.

То есть веб-сервер это некоторый “контейнер” для веб-приложений. Это логично, т.к. мы хотим писать логику веб-приложения, т.е. какие странички увидит пользователь и как они должны реагировать на действия пользователя. И мы не хотим писать код того, как будет отправляться сообщение пользователю, как будут передаваться байты информации и другие низкоуровневые и очень требовательные к качеству реализации вещи.

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

А чтобы связать эти две части, т.е. веб-сервер и веб-приложение, нужен контракт их взаимодействия, т.е. по каким правилам они это будут делать. Чтобы как-то описать контракт, как должно выглядеть взаимодействие между веб-приложением и веб-сервером и придуман Servlet API.

Интересно, что даже если вы используете фрэймворки вроде Spring, то “под капотом” всё равно работает Servlet API. То есть вы используете Spring, а Spring за Вас работает с Servlet API.

Получается, что наш проект веб-приложения должен зависеть (depends on)

от Servlet API. В этом случае Servlet API будет зависимостью (dependency). Как мы знаем, Gradle в том числе позволяет декларативным образом описывать зависимости проекта. А то, каким образом можно управлять зависимостями, описывают плагины.

Например, Java Gradle Plugin вводит способ управления зависимостями “testImplementation”, который говорит, что такая зависимость нужна только для тестов.

А вот Gradle War Plugin добавляет способ управления зависимостями “providedCompile”, который говорит, что такая зависимость не будет включена в WAR архив нашего веб-приложения.

Почему мы не включаем Servlet API в наш WAR архив? Потому что Servlet API будет предоставлен нашему веб-приложению самим веб-сервером.

providedCompile

.

Таким образом, блок зависимостей (dependencies) будет иметь следующий вид:


dependencies {
    providedCompile 'javax.servlet:javax.servlet-api:3.1.0'
    testImplementation 'junit:junit:4.12'
}

Итак, вернёмся к web.xml файлу. По умолчанию, Gradle не создаёт никакой Deployment Descriptor, поэтому нам нужно сделать это самостоятельно.

Создадим каталог

src/main/webapp/WEB-INF

, а в нём создадим XML файл с названием

web.xml

.

Теперь давайте откроем саму спецификацию “Java Servlet Specification” и главу ”

“.

Как сказано в “14.3 Deployment Descriptor”, XML документ Deployment Descriptor’а описан схемой

Запуск

Пробуем запустить проект.

Видим, что новый пользователь появился в базе данных. Пароль зашифрован.

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

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

Кастомный вход пользователя

Самостоятельно написанный

AuthProvider

позволит пользователю входить не только по email, но и по имени пользователя.

Контроллеры

В нашем сервисе заметок будут следующие страницы:

  • Главная;
  • Регистрация;
  • Вход;
  • Список заметок пользователя.

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

Создадим пакет

controllers

, в нем класс

IndexController

, содержащий обычный get-mapping главной страницы.

Класс

RegistrationController

отвечает за регистрацию пользователя. Post-mapping принимает данные из формы, сохраняет пользователя в базу данных и переадресовывает на страницу входа.

PasswordEncoder

будет описан позднее. Он используется для шифрования паролей.

Конфигурация фильтра и application.properties

Внедрим инъекцию OAuth2ClientContext, чтобы использовать ее в нашей конфигурации безопасности.


@Autowired
private OAuth2ClientContext oAuth2ClientContext;

OAuth2ClientContext используется при создании фильтра, который проверяет пользовательский запрос на вход через соцсеть. Фильтр доступен благодаря аннотации @EnableOAuth2Client. Все, что нам нужно, вызвать его в правильном порядке,

до

основного фильтра Spring Security. Только в таком случае мы сможем поймать перенаправления в процессе входа с OAuth2. Для этого используем

FilterRegistrationBean

, в котором выставляем приоритет нашего фильтра на -100.

Настройка oauth2 на примере google в spring security

. Для поддержки OAuth2 добавим в pom.xml следующую библиотеку:


Модифицируем нашу конфигурацию Spring Security в классе

SecurityConfig

Настройка spring security для классического входа

Spring Security помогает нам защищать приложение и его ресурсы от несанкционированного доступа. Мы создадим лаконичную рабочую конфигурацию в классе

SecurityConfig

, унаследованный от

WebSecurityConfigurerAdapter

, который поместим в пакет

config

. Пометим его аннотацией @EnableWebSecurity, которая включит поддержку Spring Security, и аннотацией @Configuration, которая указывает, что этот класс содержит некую конфигурацию.

Примечание:

в автоматически сконфигурированном pom.xml стояла версия родительского компонента Spring Boot 2.1.4.RELEASE, что мешало внедрить Security устоявшимся способом. Во избежание конфликтов в проекте рекомендуется изменить версию на 2.0.1.RELEASE.

Репозитории

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

repos

, в нем создадим интерфейсы

Создание проекта

Идем на

и формируем основу проекта:

  • Web — запуск приложения на встроенном Tomcat, url-сопоставления и тому подобное;
  • JPA — связь с базой данных;
  • Mustache — шаблонизатор, используется для генерации веб-страниц;
  • Security — защита приложения. То, ради чего эта статья и создавалась.

Скачиваем получившийся архив и распаковываем в нужной вам папке. Запускаем его в IDE.

Вы можете выбирать БД на свое усмотрение. В качестве базы данных для проекта я использую MySQL, поэтому в файл pom.xml в блок добавляю следующую зависимость:

Конфигурация application.properties на данный момент следующая:

Страницы

Для создания страниц я использую

. Вы можете внедрить и другой, не принципиально. Для мета-информации, которая используется на всех страницах, создан файл meta.mustache. В нем же подключен Bootstrap, чтобы страницы нашего проекта выглядели симпатичней. Страницы создаются в директории “src/main/resources/templates”. Файлы имеют расширение mustache. Размещение html-кода непосредственно в статье сделает ее слишком большой, поэтому вот

Сущности

Создадим пакет

entities

, в который поместим сущности базы данных. Пользователь будет описываться классом

Усовершенствуем контроллер

Мы настроили Spring Security. Самое время воспользоваться этим в контроллере заметок. Теперь каждый mapping будет принимать дополнительный параметр Principal, по которому постарается найти пользователя. Почему нельзя напрямую внедрить класс

Итоговый запуск проекта

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

Пользователь успешно входит и через обычную форму, и через Google аккаунт. Этого мы и добивались!

Надеюсь, эта статья прояснила определенные моменты в создании веб-приложения, его защите с помощью Spring Security и комбинировании разных способов входа. C полным кодом проекта вы можете

Похожее:  Авторизация пользователей на MODX Login - ИТ Шеф

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *