How can I have list of all users logged in (via spring security) my web application
I'm using spring security in my web application, and now I want to have a list of all users who are logged in my program.
How can I have access to that list? Aren't they already kept somewhere within spring framework? Like SecurityContextHolder or SecurityContextRepository?
For accessing the list of all logged in users you need to inject SessionRegistry instance to your bean.
@Autowired
@Qualifier("sessionRegistry")
private SessionRegistry sessionRegistry;
And then using injcted SessionRegistry you can access the list of all principals:
List<Object> principals = sessionRegistry.getAllPrincipals();
List<String> usersNamesList = new ArrayList<String>();
for (Object principal: principals) {
if (principal instanceof User) {
usersNamesList.add(((User) principal).getUsername());
}
}
But before injecting session registry you need to define session management part in your spring-security.xml (look at Session Management section in Spring Security reference documentation) and in concurrency-control section you should set alias for session registry object (session-registry-alias) by which you will inject it.
<security:http access-denied-page="/error403.jsp" use-expressions="true" auto-config="false">
<security:session-management session-fixation-protection="migrateSession" session-authentication-error-url="/login.jsp?authFailed=true">
<security:concurrency-control max-sessions="1" error-if-maximum-exceeded="true" expired-url="/login.html" session-registry-alias="sessionRegistry"/>
</security:session-management>
...
</security:http>
In JavaConfig, it would look like this:
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(final HttpSecurity http) throws Exception {
// ...
http.sessionManagement().maximumSessions(1).sessionRegistry(sessionRegistry());
}
@Bean
public SessionRegistry sessionRegistry() {
return new SessionRegistryImpl();
}
@Bean
public ServletListenerRegistrationBean<HttpSessionEventPublisher> httpSessionEventPublisher() {
return new ServletListenerRegistrationBean<HttpSessionEventPublisher>(new HttpSessionEventPublisher());
}
}
With the calling code looking like this:
public class UserController {
@Autowired
private SessionRegistry sessionRegistry;
public void listLoggedInUsers() {
final List<Object> allPrincipals = sessionRegistry.getAllPrincipals();
for(final Object principal : allPrincipals) {
if(principal instanceof SecurityUser) {
final SecurityUser user = (SecurityUser) principal;
// Do something with user
System.out.println(user);
}
}
}
}
Note that SecurityUser
is my own class which implements UserDetails
.
Please correct me if I'm wrong.
I think @Adam's answer is incomplete. I noticed that sessions already expired in the list were appearing again.
public class UserController {
@Autowired
private SessionRegistry sessionRegistry;
public void listLoggedInUsers() {
final List<Object> allPrincipals = sessionRegistry.getAllPrincipals();
for (final Object principal : allPrincipals) {
if (principal instanceof SecurityUser) {
final SecurityUser user = (SecurityUser) principal;
List<SessionInformation> activeUserSessions =
sessionRegistry.getAllSessions(principal,
/* includeExpiredSessions */ false); // Should not return null;
if (!activeUserSessions.isEmpty()) {
// Do something with user
System.out.println(user);
}
}
}
}
}
Hope it helps.
Please correct me if I'm wrong too.
I think @Adam's and @elysch`s answer is incomplete. I noticed that there are needed to add listener:
servletContext.addListener(HttpSessionEventPublisher.class);
to
public class AppInitializer implements WebApplicationInitializer {
@Override
public void onStartup(ServletContext servletContext) {
...
servletContext.addListener(HttpSessionEventPublisher.class);
}
with security conf:
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(final HttpSecurity http) throws Exception {
// ...
http.sessionManagement().maximumSessions(1).sessionRegistry(sessionRegistry());
}
@Bean
public SessionRegistry sessionRegistry() {
return new SessionRegistryImpl();
}
@Bean
public HttpSessionEventPublisher httpSessionEventPublisher() {
return new HttpSessionEventPublisher();
}
}
And then you will get current online users!
You need to inject SessionRegistry
(as mentioned eariler) and then you can do it in one pipeline like this:
public List<UserDetails> findAllLoggedInUsers() {
return sessionRegistry.getAllPrincipals()
.stream()
.filter(principal -> principal instanceof UserDetails)
.map(UserDetails.class::cast)
.collect(Collectors.toList());
}