I’m using Spring Security and I would like to know which users are currently online. I first tried the approach using SessionRegistryImpl and <session-management session-authentication-strategy-ref="..." ... />, but I guess this List is stored in memory and I would like to avoid it (it’s going to be a huge website and a lot of users will be online at the same time, the List can become huge). Please correct me if I’m wrong.
The second approach I tried is using a listener and the HttpSessionListener interface and a custom AuthenticationManager and storing the “is online flag” in the database. Basically, the flag is set to true in the authenticate(...) method of my authentication manager and set to false in the sessionDestroyed(...) method of my session listener.
web.xml:
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
version="2.5">
<display-name>Test</display-name>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/security.xml</param-value>
</context-param>
<!-- Security -->
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<listener>
<listener-class>my.package.SessionListener</listener-class>
</listener>
<servlet>
<servlet-name>test</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>test</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<session-config>
<session-timeout>1</session-timeout>
</session-config>
</web-app>
Spring Security configuration:
<beans:beans xmlns="http://www.springframework.org/schema/security"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.1.xsd">
<beans:bean id="authenticationManager" class="my.package.security.AuthenticationManager" />
<http disable-url-rewriting="true" authentication-manager-ref="authenticationManager">
<!--<intercept-url pattern="/" access="ROLE_ANONYMOUS" />-->
<intercept-url pattern="/login*" access="ROLE_ANONYMOUS" />
<intercept-url pattern="/favicon.ico" access="ROLE_ANONYMOUS" />
<intercept-url pattern="/*" access="ROLE_USER" />
<form-login login-processing-url="/authorize" login-page="/login" authentication-failure-url="/login-failed" />
<logout logout-url="/logout" logout-success-url="/login" />
<remember-me data-source-ref="dataSource" />
</http>
</beans:beans>
my.package.SessionListener:
public class SessionListener implements HttpSessionListener
{
public void sessionCreated(HttpSessionEvent httpSessionEvent)
{
}
public void sessionDestroyed(HttpSessionEvent httpSessionEvent)
{
UserJpaDao userDao = WebApplicationContextUtils.getWebApplicationContext(httpSessionEvent.getSession().getServletContext()).getBean(UserJpaDao.class);
Authentication a = SecurityContextHolder.getContext().getAuthentication();
if(a != null)
{
User loggedInUser = userDao.findByAlias(a.getName());
if(loggedInUser != null)
{
loggedInUser.setOnline(false);
userDao.save(loggedInUser);
}
}
}
}
my.package.security.AuthenticationManager:
public class AuthenticationManager implements org.springframework.security.authentication.AuthenticationManager
{
@Autowired
UserJpaDao userDao;
public Authentication authenticate(Authentication authentication) throws AuthenticationException
{
User loggedInUser = null;
Collection<? extends GrantedAuthority> grantedAuthorities = null;
...
loggedInUser = userDao.findByAlias(authentication.getName());
if(loggedInUser != null)
{
// Verify password etc.
loggedInUser.setOnline(true);
userDao.save(loggedInUser);
}
else
{
loggedInUser = null;
throw new BadCredentialsException("Unknown username");
}
return new UsernamePasswordAuthenticationToken(loggedInUser, authentication.getCredentials(), grantedAuthorities);
}
}
sessionCreated and sessionDestroyed are fired correctly, but SecurityContextHolder.getContext().getAuthentication(); is always null.
Update: almost everything is working perfectly. The only problem is that when the session expires due to timeout, SecurityContextHolder.getContext().getAuthentication() returns null in the sessionDestroyed(...) method. It works perfectly when the logout is manually triggered.
Can someone help me? Any hint is greatly appreciated.
Thank you
I decided to undertake the session registry method (only because I was not able to make the other method work). Here is my code (important parts).
web.xml:
security.xml:
my.package.security.AuthenticationManager:
my.package.dao.UserDetailsDao (this is needed only for the remember-me functionality):
my.package.UserDetails:
sessionRegistry.getAllPrincipals()will return aList<Object>“castable” toList<UserDetails>.My code to get a list of online users, where the objects of type class
Userare persisted in the database through JPA:Note:
sessionRegistryis an Autowired implementation of the classSessionRegistryImpl.Note: for the remember-me functionality, I’m using the persistent token approach. A
persistent_loginsis needed in the database (see 10.3 Persistent Token Approach).Hope this may be useful to someone else.