Перейти к основному содержимому

Отслеживайте зарегистрированных пользователей с помощью Spring Security

· 5 мин. чтения

1. Обзор

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

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

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

2. Магазин активных пользователей

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

public class ActiveUserStore {

public List<String> users;

public ActiveUserStore() {
users = new ArrayList<String>();
}

// 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<String> users = activeUserStore.getUsers();
LoggedUser user = (LoggedUser) event.getValue();
if (!users.contains(user.getUsername())) {
users.add(user.getUsername());
}
}

@Override
public void valueUnbound(HttpSessionBindingEvent event) {
List<String> 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. Пользователи.html

<html>
<body>
<h2>Currently logged in users</h2>
<div th:each="user : ${users}">
<p th:text="${user}">user</p>
</div>
</body>
</html>

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

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

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

@Autowired
private SessionRegistry sessionRegistry;

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

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

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

...

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

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

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

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