Acompanhe os usuários conectados com o Spring Security
1. Visão geral
Neste tutorial rápido, vamos mostrar um exemplo de como podemostrack the currently logged in users in an application using Spring Security.
Para esse fim, acompanharemos uma lista de usuários conectados, adicionando o usuário quando eles fizerem login e removendo-os quando eles saírem.
UsaremosHttpSessionBindingListener para atualizar a lista de usuários conectados sempre que as informações do usuário forem adicionadas à sessão ou removidas da sessão com base nos registros do usuário no sistema ou logout do sistema.
2. Loja de usuários ativos
Para simplificar, definiremos uma classe que atua como um armazenamento na memória para os usuários conectados:
public class ActiveUserStore {
public List users;
public ActiveUserStore() {
users = new ArrayList();
}
// standard getter and setter
}
Vamos definir isso como um bean padrão no contexto do Spring:
@Bean
public ActiveUserStore activeUserStore(){
return new ActiveUserStore();
}
3. OHTTPSessionBindingListener
Agora, vamos fazer uso da interfaceHTTPSessionBindingListener e criar uma classe wrapper para representar um usuário que está conectado no momento.
Basicamente, ele ouvirá eventos do tipoHttpSessionBindingEvent, que são acionados sempre que um valor é definido ou removido, ou, em outras palavras, vinculado ou não vinculado à sessão 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
}
O ouvinte tem dois métodos que precisam ser implementados,valueBound()evalueUnbound() para os dois tipos de ações que acionam o evento que está ouvindo. Sempre que um valor do tipo que implementa o ouvinte for definido ou removido da sessão, ou a sessão for invalidada, esses dois métodos serão chamados.
Em nosso caso, o métodovalueBound() será chamado quando o usuário efetuar login e o métodovalueUnbound() será chamado quando o usuário efetuar logout ou quando a sessão expirar.
Em cada um dos métodos, recuperamos o valor associado ao evento e adicionamos ou removemos o nome de usuário da nossa lista de usuários conectados, dependendo se o valor foi vinculado ou não à sessão.
4. Rastreamento de login e logout
Agora precisamos acompanhar quando o usuário efetuou login ou logout com sucesso, para que possamos adicionar ou remover um usuário ativo da sessão. Em um aplicativo Spring Security, isso pode ser obtido implementando as interfacesAuthenticationSuccessHandlereLogoutSuccessHandler.
4.1. ImplementandoAuthenticationSuccessHandler
Para a ação de login, definiremos o nome de usuário do usuário que faz login como um atributo na sessão, substituindo o métodoonAuthenticationSuccess() que nos fornece acesso aos objetossessioneauthentication:
@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. ImplementandoLogoutSuccessHandler
Para a ação de logout, removeremos o atributo do usuário substituindo o métodoonLogoutSuccess() da interfaceLogoutSuccessHandler:
@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. Controlador e Visualização
Para ver todos os itens acima em ação, criaremos um mapeamento de controlador para a URL“/users” que irá recuperar a lista de usuários, adicioná-la como um atributo de modelo e retornar a visualizaçãousers.html:
5.1. Controlador
@Controller
public class UserController {
@Autowired
ActiveUserStore activeUserStore;
@RequestMapping(value = "/loggedUsers", method = RequestMethod.GET)
public String getLoggedUsers(Locale locale, Model model) {
model.addAttribute("users", activeUserStore.getUsers());
return "users";
}
}
5.2. Users.html
Currently logged in users
user
6. Método alternativo usandoSessionregistry
Outro método de recuperar os usuários conectados no momento é aproveitandoSessionRegistry do Spring, que é uma classe que gerencia usuários e sessões. Esta classe possui o métodogetAllPrincipals() para obter a lista de usuários.
Para cada usuário, podemos ver uma lista de todas as suas sessões chamando o métodogetAllSessions(). Para obter apenas os usuários conectados no momento, temos que excluir as sessões expiradas, definindo o segundo parâmetro degetAllSessions() parafalse:
@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());
}
Para usar a classeSessionRegistry, temos que definir o bean e aplicá-lo ao gerenciamento de sessão conforme mostrado abaixo:
http
.sessionManagement()
.maximumSessions(1).sessionRegistry(sessionRegistry())
...
@Bean
public SessionRegistry sessionRegistry() {
return new SessionRegistryImpl();
}
7. Conclusão
Neste artigo, demonstramos como podemos determinar quem são os usuários atualmente conectados a um aplicativo Spring Security.
A implementação deste tutorial pode ser encontrada emthe GitHub project - este é um projeto baseado em Maven, portanto, deve ser fácil de importar e executar como está.