Некоторые вещи лучше держать в секрете. Это относится и к данным тоже. В этом уроке вы узнаете, как реализовать простую систему контроля доступа с использованием простого 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”