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, поэтому его легко импортировать и запускать как есть.