Некоторые вещи лучше держать в секрете. Это относится и к данным тоже. В этом уроке вы узнаете, как реализовать простую систему контроля доступа с использованием простого Vaadin и Java.
Мы делаем это в четыре шага: создаем представление входа, авторизуем пользователя, аутентифицируем пользователя и создаем кнопку выхода.
В этом руководстве не рассматривается, как хранятся учетные данные пользователя, а также как защитить статические ресурсы, такие как CSS и изображения.
Методы, используемые здесь, могут быть применены к большинству проектов Vaadin, но они написаны для Простого Java-сервлета стартера, найденного по адресу vaadin.com/start , который на момент написания использует версию Vaadin 14.1.5
.
После загрузки и распаковки проекта его можно запустить с помощью mvn jetty:выполнить
из командной строки. Приложение и Основной вид
затем доступны по адресу localhost:8080
.
Создание экрана входа в систему
Для входа в систему требуется экран входа в систему. Для простоты мы используем ЛогинФорм Ваадина .
Давайте создадим класс LoginView
, который расширяет Вертикальный макет
и имеет закрытое поле, содержащее Форму входа
.
public class LoginView extends VerticalLayout { private LoginForm loginForm; }
Рекомендуется отложить инициализацию компонента до тех пор, пока он не будет подключен. Это позволяет избежать потенциально дорогостоящего кода для компонента, который никогда не отображается пользователю. Из-за Ошибки потока #4595 конструктор запускается, даже если доступ к компоненту ограничен через событие BeforeEnterEvent
.
Мы можем выполнить инициализацию, переопределив метод onAttach
. Чтобы запустить его только один раз, мы проверяем, является ли это первым attachevent, используя метод attachEvent#is Initial Attach
.
Чтобы запустить его только один раз, мы проверяем, является ли это первым attachevent, используя метод || attachEvent#is Initial Attach ||.
@Override public void onAttach(AttachEvent event) { if (event.isInitialAttach()) { initialize(); } }
Затем мы можем создать метод инициализации, в котором мы создадим экземпляр формы входа. Мы выравниваем форму по центру, как по горизонтали, так и по вертикали, а затем скрываем Забыли пароль?
опция из формы входа в систему перед добавлением ее в макет.
|| опция из формы входа в систему перед добавлением ее в макет.
private void initialize() { setAlignItems(Alignment.CENTER); setJustifyContentMode(JustifyContentMode.CENTER); setSizeFull(); loginForm = new LoginForm(); loginForm.setForgotPasswordButtonVisible(false); add(loginForm); }
Чтобы включить навигацию к представлению, мы помечаем его с помощью @Route
. Если маршрут не указан, маршрутом будет имя класса без “представления”, в данном случае логин
.
Событие входа в систему может быть обработано этим представлением путем реализации ComponentEventListener<Абстрактный вход в систему. Событие входа в систему>
, и в методе инициализации путем добавления представления в качестве прослушивателя с помощью Формы входа.addLoginListener(this);
.
На данный момент метод прослушивателя событий компонента может просто установить ошибку в форме, которая отображает сообщение о том, что имя пользователя или пароль неверны.
На данный момент метод прослушивателя событий компонента может просто установить ошибку в форме, которая отображает сообщение о том, что имя пользователя или пароль неверны.
@Override public void onComponentEvent(AbstractLogin.LoginEvent loginEvent) { loginForm.setError(true); }
После восстановления или перезапуска приложения теперь мы можем перейти к http://localhost:8080/login
, и убедитесь, что независимо от имени пользователя и пароля, которые мы вводим, мы всегда получаем сообщение об ошибке.
Авторизация пользователя
Авторизация – это процесс определения того, может ли пользователь выполнить определенное действие. В нашем случае пользователь имеет право доступа к любому представлению, если он прошел проверку подлинности. Авторизация выполняется перед входом в любое представление.
Давайте начнем с создания класса Служба безопасности
, которая отвечает за проверку подлинности пользователя. Мы делаем этот класс одноэлементным, создавая частный конструктор и возвращая статический экземпляр в методе getInstance()
.
Метод getInstance()
может быть синхронизирован
для защиты от создания нескольких экземпляров из-за одновременного доступа. В этом случае, поскольку в классе не сохраняется состояние, мы опускаем его для повышения производительности.
public class SecurityService { private static SecurityService instance; private SecurityService() {} public static SecurityService getInstance() { if (instance == null) { instance = new SecurityService(); } return instance; } }
Когда пользователь входит в систему, мы сохраняем имя пользователя в качестве атрибута сеанса под произвольным ключом. Таким образом, мы можем проверить, вошел ли пользователь в систему, проверив, установлен ли этот атрибут.
Таким образом, мы можем проверить, вошел ли пользователь в систему, проверив, установлен ли этот атрибут.
// This can be anything, but we want to avoid accidentally using the same name for different purposes private static final String USER_ATTRIBUTE = "SecurityService.User"; ... public boolean isAuthenticated() { return VaadinSession.getCurrent() != null && VaadinSession.getCurrent().getAttribute(USER_ATTRIBUTE) != null; }
Далее нам нужно вызвать этот метод перед входом в какое-либо представление. Для этого мы используем Перед вводом списка
, добавляемый к каждому Пользовательский интерфейс
. Мы можем использовать vaadinservice InitListener , чтобы добавлять его всякий раз, когда создается ПОЛЬЗОВАТЕЛЬСКИЙ интерфейс
.
Мы создаем класс, реализующий интерфейс vaadinservice InitListener
, и реализуем метод serviceinit
.
public class MyServiceInitListener implements VaadinServiceInitListener { @Override public void serviceInit(ServiceInitEvent serviceInitEvent) { } }
Чтобы это было зарегистрировано при запуске, мы должны создать каталог src/main/resources/META-INF/services/
и добавить файл с именем com.vaadin.flow.server. ВаадИнсервис инициализатор
. Единственным содержимым файла должно быть полное имя нашего сервиса initlistener, в нашем случае org.vaadin.erik. Мой сервис для прослушивания
.
Теперь, когда мы подключились к инициализации службы, мы можем использовать событие инициализации для прослушивания инициализаций UI
. Затем мы изменим наш класс, чтобы также реализовать Прослушиватель инициализации пользовательского интерфейса
, и зарегистрируем его через Сервис InitEvent
.
Затем мы изменим наш класс, чтобы также реализовать ||Прослушиватель инициализации пользовательского интерфейса ||, и зарегистрируем его через || Сервис InitEvent ||.
@Override public void serviceInit(ServiceInitEvent serviceInitEvent) { serviceInitEvent.getSource().addUIInitListener(this); }
Теперь мы идем дальше по кроличьей норе, реализуя метод Инициализации пользовательского интерфейса
для добавления класса в качестве BeforeEnterListener
во вновь созданный пользовательский интерфейс
. Для этого нам также необходимо реализовать Перед EnterListener
.
Для этого нам также необходимо реализовать || Перед EnterListener ||.
@Override public void uiInit(UIInitEvent uiInitEvent) { uiInitEvent.getUI().addBeforeEnterListener(this); }
Наконец, мы реализуем метод beforerender
для проверки подлинности пользователя, а если нет, перенаправляем пользователя в режим входа в систему.
Наконец, мы реализуем метод || beforerender|| для проверки подлинности пользователя, а если нет, перенаправляем пользователя в режим входа в систему.
@Override public void beforeEnter(BeforeEnterEvent beforeEnterEvent) { boolean authenticated = SecurityService.getInstance().isAuthenticated(); if (beforeEnterEvent.getNavigationTarget().equals(LoginView.class)) { if (authenticated) { beforeEnterEvent.forwardTo(MainView.class); } return; } if (!authenticated) { beforeEnterEvent.forwardTo(LoginView.class); } }
При переходе к Просмотру входа
код включает особый случай, чтобы избежать зацикливания на повторной пересылке пользователя в LoginView
, снова и снова вызывая метод beforeEnter
. Вместо этого, если пользователь вошел в систему, он перенаправляется в Главное представление
. В противном случае никаких действий не предпринимается.
Запустив приложение сейчас, вы можете увидеть, что Основной вид
больше недоступен, и перейдите к http://localhost:8080
перенаправляет вас в режим входа в систему.
Аутентификация пользователя
Теперь мы можем перейти к аутентификации пользователя, определив, являются ли предоставленные имя пользователя и пароль правильными или нет.
Мы добавляем метод в Службу безопасности
под названием аутентифицировать
который принимает имя пользователя и пароль в качестве аргументов и возвращает true
если аутентификация прошла успешно. Если это так, он также сохраняет имя пользователя в сеансе.
В этом руководстве не рассматривается процесс аутентификации в деталях, вместо этого мы храним имя пользователя и пароль непосредственно в файле класса. Это означает, что любой, у кого есть доступ к исходному коду, может увидеть учетные данные. Как минимум, пароль должен быть хеширован с помощью функции хеширования, такой как Bcrypt
.
Как минимум, пароль должен быть хеширован с помощью функции хеширования, такой как || Bcrypt||.
private static final String USERNAME = "admin"; private static final String PASSWORD = "password"; public boolean authenticate(String username, String password) { if (USERNAME.equals(username) && PASSWORD.equals(password)) { VaadinSession.getCurrent().setAttribute(USER_ATTRIBUTE, username); return true; } return false; }
В представлении входа в систему мы вызываем этот метод и устанавливаем ошибку только в том случае, если метод возвращает false. В противном случае мы перенаправляем пользователя в Основной вид
. Поскольку мы теперь жестко запрограммировали значение MainView.class
дважды в проекте мы должны добавить метод в службу безопасности, который возвращает представление по умолчанию, и изменить предыдущие способы использования, чтобы использовать этот новый метод.
Поскольку мы теперь жестко запрограммировали значение || MainView.class || дважды в проекте мы должны добавить метод в службу безопасности, который возвращает представление по умолчанию, и изменить предыдущие способы использования, чтобы использовать этот новый метод.
@Override public void onComponentEvent(AbstractLogin.LoginEvent loginEvent) { boolean success = SecurityService.getInstance() .authenticate(loginEvent.getUsername(), loginEvent.getPassword()); if (success) { UI.getCurrent().navigate(SecurityService.getInstance().getDefaultView()); } else { loginForm.setError(true); } }
Поскольку мы теперь жестко запрограммировали значение || MainView.class || дважды в проекте мы должны добавить метод в службу безопасности, который возвращает представление по умолчанию, и изменить предыдущие способы использования, чтобы использовать этот новый метод.
public Class extends Component> getDefaultView() { return MainView.class; }
На этом этапе мы можем войти в приложение с учетными данными admin
/|/пароль . Однако мы пока не можем выйти из системы.
Выход из системы
Чтобы иметь возможность выйти из системы, нам нужен метод в Службе безопасности
для выхода из системы. Этот метод делает две вещи:
- Он делает недействительным завернутый
HttpSession
, закрывая все экземплярыVaadinSession
, которые он содержит. Это приводит к созданию новойVaadinSession
при следующей навигации, эффективно очищая сохраненного пользователя. - Он перенаправляет пользователя в режим входа в систему.
Он перенаправляет пользователя в режим входа в систему.
public void logOut() { VaadinSession.getCurrent().getSession().invalidate(); UI.getCurrent().navigate(LoginView.class); }
Все сервлеты в WAR-файле используют один и тот же Сеанс HTTPS , но имеют свои собственные Ваадинсессия экземпляры. Если вы хотите закрыть только определенную Vaadinsession, позвоните VaadinSession.GetCurrent().закрыть() .
Теперь нам нужно откуда-то вызвать этот метод. Мы добавляем кнопку для выхода из системы в Главное представление
и в то же время переносим всю инициализацию в onAttach
, как мы сделали в представлении входа. Мы также удаляем компоненты по умолчанию из основного представления.
Мы также удаляем компоненты по умолчанию из основного представления.
private void initialize() { Button logOutButton = new Button("Log out"); logOutButton.getStyle().set("margin-left", "auto"); logOutButton.addClickListener(e -> SecurityService.getInstance().logOut()); HorizontalLayout toolbar = new HorizontalLayout(logOutButton); toolbar.setWidthFull(); add(toolbar); add(new Span("Hello, you are logged in")); } public void onAttach(AttachEvent event) { if (event.isInitialAttach()) { initialize(); } }
Кнопка просто вызывает метод Выход
. Мы устанавливаем левое поле равным авто
, чтобы переместить кнопку вправо.
Когда мы сейчас тестируем приложение, мы сначала не можем получить доступ к Главному представлению
. После входа в систему, но только с правильными учетными данными, мы переходим к основному представлению. Переход к представлению входа в систему теперь возвращает нас прямо к основному представлению.
После нажатия кнопки выхода из системы мы больше не сможем получить доступ к главному представлению, если не войдем в систему снова.
Поздравляем, контроль доступа теперь настроен в вашем приложении!
Полный код можно найти на GitHub .
Изображение на обложке dying_grotesque лицензирован в соответствии с CC BY 2.0
Оригинал: “https://dev.to/eriklumme/simple-access-control-in-vaadin-53d0”