Gardez une trace des utilisateurs connectés avec Spring Security

Gardez une trace des utilisateurs connectés avec Spring Security

1. Vue d'ensemble

Dans ce rapide tutoriel, nous allons montrer un exemple de la façon dont nous pouvonstrack the currently logged in users in an application using Spring Security.

Pour ce faire, nous allons suivre la liste des utilisateurs connectés en ajoutant l’utilisateur lorsqu’ils se connectent et en les supprimant lorsqu’ils se déconnectent.

Nous utiliserons lesHttpSessionBindingListener pour mettre à jour la liste des utilisateurs connectés chaque fois que des informations utilisateur sont ajoutées à la session ou supprimées de la session en fonction de la connexion des utilisateurs au système ou de la déconnexion du système.

2. Magasin d'utilisateurs actif

Pour plus de simplicité, nous définirons une classe qui agit comme un magasin en mémoire pour les utilisateurs connectés:

public class ActiveUserStore {

    public List users;

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

    // standard getter and setter
}

Nous allons définir cela comme un bean standard dans le contexte Spring:

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

3. LesHTTPSessionBindingListener

Nous allons maintenant utiliser l’interfaceHTTPSessionBindingListener et créer une classe wrapper pour représenter un utilisateur actuellement connecté.

Cela écoutera essentiellement les événements de typeHttpSessionBindingEvent, qui sont déclenchés chaque fois qu'une valeur est définie ou supprimée, ou, en d'autres termes, liés ou non liés, à la session 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
}

L'écouteur a deux méthodes qui doivent être implémentées,valueBound() etvalueUnbound() pour les deux types d'actions qui déclenchent l'événement qu'il écoute. Chaque fois qu'une valeur du type qui implémente l'écouteur est définie ou supprimée de la session, ou que la session est invalidée, ces deux méthodes sont appelées.

Dans notre cas, la méthodevalueBound() sera appelée lorsque l'utilisateur se connectera et la méthodevalueUnbound() sera appelée lorsque l'utilisateur se déconnectera ou lorsque la session expirera.

Dans chacune des méthodes, nous récupérons la valeur associée à l'événement, puis ajoutons ou supprimons le nom d'utilisateur de notre liste d'utilisateurs connectés, selon que la valeur est liée ou non de la session.

4. Suivi de la connexion et de la déconnexion

Nous devons maintenant savoir quand l'utilisateur est connecté ou déconnecté avec succès pour pouvoir ajouter ou supprimer un utilisateur actif de la session. Dans une application Spring Security, cela peut être réalisé en implémentant les interfacesAuthenticationSuccessHandler etLogoutSuccessHandler.

4.1. Implémentation deAuthenticationSuccessHandler

Pour l'action de connexion, nous définirons le nom d'utilisateur de l'utilisateur se connectant comme un attribut sur la session en remplaçant la méthodeonAuthenticationSuccess() qui nous donne accès aux objetssession etauthentication:

@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. Implémentation deLogoutSuccessHandler

Pour l'action de déconnexion, nous supprimerons l'attribut utilisateur en remplaçant la méthodeonLogoutSuccess() de l'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. Contrôleur et vue

Afin de voir tout ce qui précède en action, nous allons créer un mappage de contrôleur pour l'URL“/users” qui récupérera la liste des utilisateurs, l'ajoutera comme attribut de modèle et retournera la vueusers.html:

5.1. Manette

@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éthode alternative utilisantSessionregistry

Une autre méthode pour récupérer les utilisateurs actuellement connectés consiste à tirer parti desSessionRegistry de Spring, qui est une classe qui gère les utilisateurs et les sessions. Cette classe a la méthodegetAllPrincipals() pour obtenir la liste des utilisateurs.

Pour chaque utilisateur, nous pouvons voir une liste de toutes leurs sessions en appelant la méthodegetAllSessions(). Afin d'obtenir uniquement les utilisateurs actuellement connectés, nous devons exclure les sessions expirées, en définissant le deuxième paramètre degetAllSessions() surfalse:

@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());
}

Pour utiliser la classeSessionRegistry, nous devons définir le bean et l'appliquer à la gestion de session comme indiqué ci-dessous:

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

...

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

7. Conclusion

Dans cet article, nous avons montré comment déterminer les utilisateurs actuellement connectés dans une application Spring Security.

L'implémentation de ce tutoriel peut être trouvée dansthe GitHub project - il s'agit d'un projet basé sur Maven, il devrait donc être facile à importer et à exécuter tel quel.