Статьи

Vaadin Spring Security Integration

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

 

Авторы получают Анри Сару из команды Ваадинов, которая дала ему ценную информацию .
Требования: в этой статье предполагается, что вы знакомы с Spring Security, и используются передовые концепции навигатора Vaadin.

Номинальное использование мандата Spring Security для использования подконтекстов в веб-приложении, каждое из которых затем может быть настроено для разных уровней доступа. Например, / public доступен с анонимным доступом, в то время как / private нужны некоторые специальные разрешения. К сожалению, Ваадин не работает таким образом: представления переводятся в фрагменты, а не в подтексты. С этого момента есть два варианта: либо настроить Vaadin для использования подконтекстов, либо встроить Spring Security в наше приложение. Мы будем использовать последний.

 

  • Первым шагом является создание представления формы входа, которое отправляет события входа в систему.
  • Корень подписывается на события входа в систему и обрабатывает попытки аутентификации через выделенный обработчик аутентификации.
  • Обработчику должны быть переданы логин, пароль и http-запрос. Поскольку Vaadin скрывает последнее в API, мы должны создать специальный сервлет, который сохраняет его в локальном потоке с помощью служебного класса:
public class RequestHolderApplicationServlet extends ApplicationServlet {
 
    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
 
        RequestHolder.setRequest(request);
 
        super.service(request, response);
 
        // We remove the request from the thread local, there's no reason to keep it once the work is done
        RequestHolder.clean();
    }
}

Обработчик входа использует Spring Security API для создания токена имени пользователя / пароля, необходимого для фреймворка. Затем он получает диспетчер аутентификации из контекста Spring и вызывает соответствующий метод, делегируя реальную аутентификацию настроенному бэкэнду . И последнее, но не менее важное: мы устанавливаем данные аутентификации в контексте Spring Security для последующего использования. 

public class AuthenticationService {
 
    public void handleAuthentication(String login, String password, HttpServletRequest httpRequest) {
         
        UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(login, password);
 
        token.setDetails(new WebAuthenticationDetails(httpRequest));
 
        ServletContext servletContext = httpRequest.getSession().getServletContext();
 
        WebApplicationContext wac = WebApplicationContextUtils.getRequiredWebApplicationContext(servletContext);
 
        AuthenticationManager authManager = wac.getBean(AuthenticationManager.class);
 
        Authentication authentication = authManager.authenticate(token);
 
        SecurityContextHolder.getContext().setAuthentication(authentication);
    }
}

 Не забудьте также создать и очистить контекст Spring Security. Поскольку мы уже разработали собственный сервлет, мы его обновим.

public class RequestHolderApplicationServlet extends ApplicationServlet {
 
    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
 
        SecurityContextHolder.setContext(SecurityContextHolder.createEmptyContext());
 
        RequestHolder.setRequest(request);
 
        super.service(request, response);
 
        RequestHolder.clean();
 
        SecurityContextHolder.clearContext();
    }
}

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

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

Итак, есть еще один последний шаг: мы должны связать все наши представления в навигатор и позволить последнему управлять навигацией. Кроме того, мы регистрируем его ViewChangeListener, который будет проверять учетные данные перед изменением представления.

public class ViewChangeSecurityChecker implements ViewChangeListener {
 
    @Override
    public boolean isViewChangeAllowed(ViewChangeEvent event) {
 
        if (event.getNewView() instanceof LoginView) {
 
            return true;
        }
 
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
 
        return authentication == null ? false : authentication.isAuthenticated();
    }
 
    @Override
    public void navigatorViewChanged(ViewChangeEvent event) {}
}

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

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