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\'
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>
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());
}
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
.
Found this note to be quite important and relevant:
"[21] Authentication by mechanisms which perform a redirect after authenticating (such as form-login) will not be detected by SessionManagementFilter, as the filter will not be invoked during the authenticating request. Session-management functionality has to be handled separately in these cases."
https://docs.spring.io/spring-security/site/docs/3.1.x/reference/session-mgmt.html#d0e4399
Also, apparently a lot of people have troubles getting sessionRegistry.getAllPrincipals() returning something different from an empty array. In my case, I fixed it by adding the sessionAuthenticationStrategy to my custom authenticationFilter:
@Bean
public CustomUsernamePasswordAuthenticationFilter authenticationFilter() throws Exception {
...
authenticationFilter.setSessionAuthenticationStrategy(sessionAuthenticationStrategy());
}
@Bean
public SessionRegistry sessionRegistry() {
return new SessionRegistryImpl();
}
//cf. https://stackoverflow.com/questions/32463022/sessionregistry-is-empty-when-i-use-concurrentsessioncontrolauthenticationstrate
public SessionAuthenticationStrategy sessionAuthenticationStrategy() {
List<SessionAuthenticationStrategy> stratList = new ArrayList<>();
SessionFixationProtectionStrategy concStrat = new SessionFixationProtectionStrategy();
stratList.add(concStrat);
RegisterSessionAuthenticationStrategy regStrat = new RegisterSessionAuthenticationStrategy(sessionRegistry());
stratList.add(regStrat);
CompositeSessionAuthenticationStrategy compStrat = new CompositeSessionAuthenticationStrategy(stratList);
return compStrat;
}
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!
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.