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

Следите за вошедшими в систему пользователями с помощью Spring Security

Краткое руководство по отслеживанию зарегистрированных пользователей в приложении, созданном с использованием Spring Security.

Автор оригинала: Loredana Crusoveanu.

1. Обзор

В этом кратком руководстве мы покажем пример того, как мы можем отслеживать текущих пользователей, вошедших в систему в приложении, с помощью Spring Security .

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

Мы будем использовать HttpSessionBindingListener для обновления списка зарегистрированных пользователей всякий раз, когда информация о пользователе добавляется в сеанс или удаляется из сеанса на основе входа пользователя в систему или выхода из системы.

2. Хранилище активных пользователей

Для простоты мы определим класс, который действует как хранилище в памяти для вошедших в систему пользователей:

public class ActiveUserStore {

    public List users;

    public ActiveUserStore() {
        users = new ArrayList();
    }

    // standard getter and setter
}

Мы определим это как стандартный компонент в контексте Spring:

@Bean
public ActiveUserStore activeUserStore(){
    return new ActiveUserStore();
}

3. HttpSessionBindingListener

Теперь мы собираемся использовать HttpSessionBindingListener интерфейс и создайте класс-оболочку для представления пользователя, который в данный момент вошел в систему.

Это будет в основном прослушивать события типа HttpSessionBindingEvent , которые запускаются всякий раз, когда значение задано или удалено, или, другими словами, привязано или не привязано к сеансу HTTP:

@Component
public class LoggedUser implements HttpSessionBindingListener {

    private String username; 
    private ActiveUserStore activeUserStore;
    
    public LoggedUser(String username, ActiveUserStore activeUserStore) {
        this.username = username;
        this.activeUserStore = activeUserStore;
    }
    
    public LoggedUser() {}

    @Override
    public void valueBound(HttpSessionBindingEvent event) {
        List users = activeUserStore.getUsers();
        LoggedUser user = (LoggedUser) event.getValue();
        if (!users.contains(user.getUsername())) {
            users.add(user.getUsername());
        }
    }

    @Override
    public void valueUnbound(HttpSessionBindingEvent event) {
        List users = activeUserStore.getUsers();
        LoggedUser user = (LoggedUser) event.getValue();
        if (users.contains(user.getUsername())) {
            users.remove(user.getUsername());
        }
    }

    // standard getter and setter
}

У прослушивателя есть два метода, которые необходимо реализовать: valueBound() и valueUnbound() для двух типов действий, которые запускают событие, которое он прослушивает. Всякий раз, когда значение типа, реализующего прослушиватель, устанавливается или удаляется из сеанса, или сеанс становится недействительным, эти два метода будут вызваны.

В нашем случае метод valueBound() будет вызван при входе пользователя в систему, а метод valueUnbound() будет вызван при выходе пользователя из системы или по истечении сеанса.

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

4. Отслеживание входа и выхода из системы

Теперь нам нужно отслеживать, когда пользователь успешно вошел в систему или вышел из нее, чтобы мы могли добавить или удалить активного пользователя из сеанса. В приложении Spring Security это может быть достигнуто путем реализации AuthenticationSuccessHandler и LogoutSuccessHandler интерфейсы.

4.1. Реализация AuthenticationSuccessHandler

Для действия входа в систему мы установим имя пользователя, входящего в систему, в качестве атрибута сеанса, переопределив метод onAuthenticationSuccess () , который предоставляет нам доступ к сеансу и аутентификации объектам:

@Component("myAuthenticationSuccessHandler")
public class MySimpleUrlAuthenticationSuccessHandler implements AuthenticationSuccessHandler {

    @Autowired
    ActiveUserStore activeUserStore;
    
    @Override
    public void onAuthenticationSuccess(HttpServletRequest request, 
      HttpServletResponse response, Authentication authentication) 
      throws IOException {
        HttpSession session = request.getSession(false);
        if (session != null) {
            LoggedUser user = new LoggedUser(authentication.getName(), activeUserStore);
            session.setAttribute("user", user);
        }
    }
}

4.2. Реализация LogoutSuccessHandler

Для действия выхода из системы мы удалим атрибут пользователя, переопределив метод onLogoutSuccess() интерфейса LogoutSuccessHandler :

@Component("myLogoutSuccessHandler")
public class MyLogoutSuccessHandler implements LogoutSuccessHandler{
    @Override
    public void onLogoutSuccess(HttpServletRequest request, 
      HttpServletResponse response, Authentication authentication)
      throws IOException, ServletException {
        HttpSession session = request.getSession();
        if (session != null){
            session.removeAttribute("user");
        }
    }
}

5. Контроллер и представление

Чтобы увидеть все вышесказанное в действии, мы создадим сопоставление контроллера для URL “/users” , которое извлечет список пользователей, добавит его в качестве атрибута модели и вернет users.html просмотр:

5.1. Контроллер

@Controller
public class UserController {
    
    @Autowired
    ActiveUserStore activeUserStore;

    @GetMapping("/loggedUsers")
    public String getLoggedUsers(Locale locale, Model model) {
        model.addAttribute("users", activeUserStore.getUsers());
        return "users";
    }
}

5.2. Users.html



    

Currently logged in users

user

6. Альтернативный Метод С Использованием Sessionregistry

Другой метод извлечения пользователей , вошедших в систему в данный момент, заключается в использовании Spring SessionRegistry , который является классом, управляющим пользователями и сеансами. Этот класс имеет метод getAllPrincipals() для получения списка пользователей.

Для каждого пользователя мы можем просмотреть список всех его сеансов, вызвав метод getAllSessions() . Чтобы получить только тех пользователей, которые вошли в систему в данный момент, мы должны исключить сеансы с истекшим сроком действия, установив второй параметр getAllSessions() в false :

@Autowired
private SessionRegistry sessionRegistry;

@Override
public List getUsersFromSessionRegistry() {
    return sessionRegistry.getAllPrincipals().stream()
      .filter(u -> !sessionRegistry.getAllSessions(u, false).isEmpty())
      .map(Object::toString)
      .collect(Collectors.toList());
}

Чтобы использовать класс SessionRegistry , мы должны определить компонент и применить его к управлению сеансами, как показано ниже:

http
  .sessionManagement()
  .maximumSessions(1).sessionRegistry(sessionRegistry())

...

@Bean
public SessionRegistry sessionRegistry() {
    return new SessionRegistryImpl();
}

7. Заключение

В этой статье мы продемонстрировали, как мы можем определить, кто в настоящее время зарегистрировался в приложении Spring Security.

Реализацию этого учебника можно найти в проекте GitHub – это проект на основе Maven, поэтому его должно быть легко импортировать и запускать как есть.