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

Простой контроль доступа в Ваадине

Некоторые вещи лучше держать в секрете. Это относится и к данным тоже. В этом уроке вы узнаете, как использовать imp… С тегами vaadin, java, безопасность, учебник.

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