I just released the Starter Class of "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 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. 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 an Maven based project, so it should be easy to import and run as it is.