I just announced the newSpring Security 5 modules (primarily focused on OAuth2) in the course:

>> CHECK OUT LEARN SPRING SECURITY

1. Overview

In this quick tutorial, we’re going to show an example of how we can track the currently logged in users in an application using Spring Security.

For this purpose, we’re going to keep track of a list of logged in users by adding the user when they log in and removing them when they log out.

We’ll leverage the HttpSessionBindingListener to update the list of logged in users whenever user information is added to the session or removed from the session based on user logs into the system or logs out from the system.

2. Active User Store

For simplicity, we will define a class that acts as an in-memory store for the logged in users:

public class ActiveUserStore {

    public List<String> users;

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

    // standard getter and setter
}

We’ll define this as a standard bean in the Spring context:

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

3. The HTTPSessionBindingListener

Now, we’re going to make use of the HTTPSessionBindingListener interface and create a wrapper class to represent a user that is currently logged in.

This will basically listen to events of type HttpSessionBindingEvent, which are triggered whenever a value is set or removed, or, in other words, bound or unbound, to the HTTP session:

@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
}

The listener has two methods that need to be implemented, valueBound() and valueUnbound() for the two types of actions that trigger the event it is listening for. Whenever a value of the type that implements the listener is set or removed from the session, or the session is invalidated, these two methods will be invoked.

In our case, the valueBound() method will be called when the user logs in and the valueUnbound() method will be called when the user logs out or when the session expires.

In each of the methods we retrieve the value associated with the event, then add or remove the username from our list of logged in users, depending on whether the value was bound or unbound from the session.

4. Tracking Login and Logout

Now we need to keep track of when the user is successfully logged in or logged out so that we can add or remove an active user from the session. In a Spring Security application, this can be achieved by implementing the AuthenticationSuccessHandler and LogoutSuccessHandler interfaces.

4.1. Implementing AuthenticationSuccessHandler

For the login action, we will set the username of the user logging in as an attribute on the session by overriding the onAuthenticationSuccess() method which provides us access to the session and authentication objects:

@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. Implementing LogoutSuccessHandler

For the logout action, we will remove the user attribute by override the onLogoutSuccess() method of the LogoutSuccessHandler interface:

@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. Controller and View

In order to see all the above in action, we will create a controller mapping for the URL “/users” that will retrieve the list of users, add it as a model attribute and return the users.html view:

5.1. Controller

@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

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

6. Alternative method using SessionRegistry

Another method of retrieving the currently logged in users is by leveraging Spring’s SessionRegistry, which is a class that manages users and sessions. This class has the method getAllPrincipals() to obtain the list of users.

For each user, we can see a list of all their sessions by calling the method getAllSessions(). In order to obtain only the currently logged in users, we have to exclude the expired sessions, by setting the second parameter of getAllSessions() to 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());
}

In order to use the SessionRegistry class, we have to define the bean and apply it to the session management as shown below:

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

...

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

7. Conclusion

In this article, we have demonstrated how we can determine who the currently logged in users are in a Spring Security application.

The implementation of this tutorial can be found in the GitHub project – this is a Maven based project, so it should be easy to import and run as it is.

I just announced the new Spring Security 5 modules (primarily focused on OAuth2) in the course:

>> CHECK OUT LEARN SPRING SECURITY

newest oldest most voted
Notify of
Jokes of the day
Guest
Jokes of the day

What are benefits of your solution compered to solution with SessionRegistryImpl as described here: http://stackoverflow.com/questions/11271449/how-can-i-have-list-of-all-users-logged-in-via-spring-security-my-web-applicat ?

Eugen Paraschiv
Guest

That is a very good point – I haven’t used that in a long while so I missed when giving feedback on the article. Cool – thanks for the heads up – it’s on the Content Calendar to add to the article.
Cheers,
Eugen.

Luke
Guest
Luke

Awesome tutorials I must say, just love it 🙂 But now I have a little problem with this one. User can login and in the process (onAuthenticationSuccess method) activeUserStore is updated -> user is in the store, as I see in debug mode. But then, when I call activeUserStore.getUsers() to list them on /loggedUsers page, the list is empty. Maybe I am missing some @Autowires or some configuration, not sure how to track this down really. Can anybody help? I can provide any part of my code when needed.

Loredana Crusoveanu
Guest
Loredana Crusoveanu

Hey Luke. Can you share the code where you define and inject the bean? Perhaps you’re not getting the same bean instance the second time.

Luke
Guest
Luke

Helo Loredana, thanks for your interest 🙂 Sure, here it is:

Defining the Bean in AppConfig.java

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

Then using it through @Autowire in my controllers:

@Autowired
private ActiveUserStore activeUserStore;

I reverted all my changes and I am using completely the same ActiveUserStore.java and LoggedUser.java as in the demo project here:
https://github.com/Baeldung/spring-security-registration/tree/707e0e6a84eda772d08819069cc134dc9a9a01cf/src/main/java/org/baeldung/security

When I am checking in debug mode, i really do get different Instances of the class. [email protected] is always different.

Luke
Guest
Luke

By the way, now when we are talking about instances, I see that there is some serious issue. My AppConfig class is called three times actualy and so there are three ActiveUserStores. When logging in and out, it uses the first initial instance, but when listing users through /loggedUsers, it uses second instance.

Loredana Crusoveanu
Guest
Loredana Crusoveanu

Sounds like a configuration issue then. Is you AppConfig class in a package that gets scanned more than once by other classes with an @Configuration annotation?

Luke
Guest
Luke

I guess it is somewhere in XMLs. I actualy use Thymeleaf and I have config for it declared this way: public class SpringWebConfig extends WebMvcConfigurerAdapter implements ApplicationContextAware { and it has method setApplicationContext(..) which is called three times and leads to all my problems.. I guess I got it wrong quite some time ago, it was working somehow but the problem kept getting bigger on the background as a snowball rolling from the hill. @Configures and @ComponentScans are okay I would say. When I deleted and it’s i got “only” two contexts and the app was able to run, but… Read more »

Matrix
Guest
Matrix

Hi Eugen did u write some article about spring security with JSF.
Thank you

Eugen Paraschiv
Guest

Sure – here it is.

Maxi Wu
Guest
Maxi Wu

I have a spring-boot project with spring security (java config), if I create a SessionRegistry bean in my security config class, then the autowired sessionRegistry.getAllPrinciples() return empty. I search for answer and maybe my web application is using a different sessionRegistry instance. but I don’t know how to get that instance in my @service.

Eugen Paraschiv
Guest

Hey Wu,
That may indeed be the case – but it’s unfortunately going to be difficult to answer without a lot more detail or looking at the code.
In cases like this we should do a PR on the codebase in Github, reproducing the problem (failing test would be optimal). That way I can run it and have a look.
Hope that helps. Cheers,
Eugen.