Tuesday, December 28, 2010

Spring Security - MVC: Querying the SessionRegistry

In this tutorial we will build a Spring MVC 3 application secured by Spring Security 3. We will explore how to retrieve currently logged-in users using the SessionRegistry. We will also explore how to retrieve their session information. We will focus on simple configuration. Our users will be authenticated based on Spring's built-in in-memory user-service. This tutorial will build on top of the Spring Security 3 - MVC: Using a Simple User-Service Tutorial because the only difference is mainly XML configuration. It's highly recommended that you read that first!

What is SessionRegistry?
Maintains a registry of SessionInformation instances.

Source: Spring Security 3 API for SessionRegistry
What is SessionInformation?
Represents a record of a session within the Spring Security framework.

This is primarily used for concurrent session support.

Source: Spring Security 3 API for SessionInformation
We begin by inquiring how to query the SessionRegistry. A search on Spring Security Reference 3 gives us the following information:
Setting up concurrency-control, either through the namespace or using plain beans has the useful side effect of providing you with a reference to the SessionRegistry which you can use directly within your application ...

The getAllPrincipals() method supplies you with a list of the currently authenticated users. You can list a user's sessions by calling the getAllSessions(Object principal, boolean includeExpiredSessions) method, which returns a list of SessionInformation objects. You can also expire a user's session by calling expireNow() on a SessionInformation instance.

Source: 11.3.1 Querying the SessionRegistry for currently authenticated users and their sessions
Based on this reference we need to setup the concurrency control to access the SessionRegistry.

Here's what we need to do:

1. "To use concurrent session support, you'll need to add the following to web.xml"

2. "In addition, you will need to add the ConcurrentSessionFilter to your FilterChainProxy."

We add this in the http tag

3. "The ConcurrentSessionFilter requires two properties, sessionRegistry, which generally points to an instance of SessionRegistryImpl, and expiredUrl, which points to the page to display when a session has expired.".

We add the concurrencyFilter bean and sessionRegistry bean.

4. "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."

This means we can not use the following form-login tag anymore

5. This means we set the auto-config property to false:

6. Because we disabled auto-config and removed the form-login tag, we must manually assign an AuthenticationEntryPoint:

7. And because we don't have an option to set the default success url, we must add our own handler:

8. And because we don't have an option to set the default failure url, we must add our own handler as well:

9. To activate these handlers, we need to assign them to an AuthenticationFilter:

10. The AuthenticationFilter references an authenticationManager. We are required to set this as an alias:

11. We need to replace the default AuthenticationFilter with our customized filter. We do this by adding it to the FilterChainProxy

12. Define a concrete concurrent control strategy (after all, this is what we really need to activate):

We're done with the steps.

Let's now examine our final Spring XML configurations. Remember we're still dealing with a Spring MVC application.

web.xml

spring-security.xml

spring-servlet.xml

applicationContext.xml

To test this configuration, we create a JSP that displays the a list of currently authenticated users along with their associated details.

To serve this JSP, we add a third request handler in our existing primary controller.

MainController

Notice we have injected the SessionRegistry:

To access all logged-in users, we called the following method:
sessionRegistry.getAllPrincipals()
To access all sessions of the current user, we use the following:
sessionRegistry.getAllSessions()
When we run this application, the logs show the following:
[DEBUG] Received request to show users page
[DEBUG] Total logged-in users: 2
[DEBUG] List of logged-in users: 
[DEBUG] org.springframework.security.core.userdetails.User@31a92e: Username: jane; Password: [PROTECTED]; Enabled: true; AccountNonExpired: true; credentialsNonExpired: true; AccountNonLocked: true; Granted Authorities: ROLE_USER
[DEBUG] org.springframework.security.core.userdetails.User@31dd0b: Username: john; Password: [PROTECTED]; Enabled: true; AccountNonExpired: true; credentialsNonExpired: true; AccountNonLocked: true; Granted Authorities: ROLE_ADMIN,ROLE_USER
[DEBUG] Total sessions including expired ones: 1
[DEBUG] Total sessions: 1
And here's the actual JSP page:
To access the users page, enter the following URL:
http://localhost:8080/spring-security-sessionregistry/krams/main/users
That's it. We've managed to setup a working Spring MVC 3 application that's secured by Spring Security. We've managed to enable concurrent session control and access session information of all currently authenticated users. We've just touched the surface of concurrent session control, specifically SessionRegistry.

The best way to learn further is to try the actual application.

Download the project
You can access the project site at Google's Project Hosting at http://code.google.com/p/spring3-security-mvc-integration-tutorial/

You can download the project as a Maven build. Look for the spring-security-sessionregistry.zip in the Download sections.

You can run the project directly using an embedded server via Maven.
For Tomcat: mvn tomcat:run
For Jetty: mvn jetty:run
StumpleUpon DiggIt! Del.icio.us Blinklist Yahoo Furl Technorati Simpy Spurl Reddit Google I'm reading: Spring Security - MVC: Querying the SessionRegistry ~ Twitter FaceBook

Subscribe by reader Subscribe by email Share

21 comments:

  1. how about counting online visitor? sum of members and client?

    ReplyDelete
  2. @sown, I believe those encompasses all of them. When you say visitor, members, client I think these are categorized as ROLES.

    ReplyDelete
  3. suppose that i have a list of all users that signed in to system and now are online.
    i nedd to expire some of them.
    how to expire session?
    i know that i can remove session from sessionRegistryImpl onject by using sessionRegistry.removeSessionInformation().
    but it seems not enough and also must be removed from SecurityContextHolder.
    can you have any ideas?

    ReplyDelete
  4. Call the expireNow() form the SessionInformation. I mentioned that in this tutorial.

    "etting up concurrency-control, either through the namespace or using plain beans has the useful side effect of providing you with a reference to the SessionRegistry which you can use directly within your application ...

    The getAllPrincipals() method supplies you with a list of the currently authenticated users. You can list a user's sessions by calling the getAllSessions(Object principal, boolean includeExpiredSessions) method, which returns a list of SessionInformation objects. You can also expire a user's session by calling expireNow() on a SessionInformation instance.

    Source: 11.3.1 Querying the SessionRegistry for currently authenticated users and their sessions"

    ReplyDelete
  5. Using Spring 3.1.0 RC3 I get

    The type GrantedAuthorityImpl is deprecated ,

    and does
    public Collection getAuthorities(Integer access) {

    should be

    public Collection getAuthorities(Integer access) {

    public Collection ?

    Thanks

    ReplyDelete
  6. oops delete my last comment ,fix :

    using Spring Security 3.1.0 RC3 I get

    The type GrantedAuthorityImpl is deprecated ,

    does

    public Collection>grantedauthority< getAuthorities

    should be

    public Collection>GrantedAuthority< getAuthorities?

    ( In Comments I cant put the < > ,it erase it )
    Thanks

    ReplyDelete
  7. I haven't checked the latest changes in Spring Security 3.1.0.RC3 for this tutorial, though I have implemented it on other projects. If indeed that class is deprecated, I suggest you use the latest implementation. It should be relatively the same.

    ReplyDelete
  8. This comment has been removed by the author.

    ReplyDelete
  9. This comment has been removed by the author.

    ReplyDelete
  10. First off, thanks so much for all the tutorials...they've all been a massive help.

    My understanding is that with the http auto-config set to "false" I'll need to configure remember-me myself. I tried adding the following:

    security:custom-filter position="REMEMBER_ME_FILTER" ref="rememberMeFilter"

    bean id="rememberMeFilter" class="org.springframework.security.web.authentication.rememberme.RememberMeAuthenticationFilter"
    p:authenticationManager-ref="authenticationManager"
    p:rememberMeServices-ref="rememberMeServices"

    bean id="rememberMeServices" class="org.springframework.security.web.authentication.rememberme.TokenBasedRememberMeServices"
    p:userDetailsService-ref="userManager"
    p:key="whatever"

    I also added p:rememberMeServices-ref="rememberMeServices" to the authenticationFilter.

    This configuration doesn't seem to work...any suggestions?

    ReplyDelete
  11. This comment has been removed by the author.

    ReplyDelete
  12. I thought this might help others out so here's what I did to get "remember me" working. If anyone notices anything I could have done better (or that is outright wrong) please let me know. Also, sorry for all the deleted comments.

    In <http> I removed
    <logout />
    and added
    <security:custom-filter position="REMEMBER_ME_FILTER" ref="rememberMeFilter" />
    <security:custom-filter position="LOGOUT_FILTER" ref="logoutFilter" />

    Added the following property to the "authenticationFilter" bean:
    p:rememberMeServices-ref="rememberMeServices"

    Added the following provider to "authenticationManager" (after the existing provider):
    <security:authentication-provider ref="rememberMeAuthenticationProvider" />

    Added the following beans:
    <bean id="rememberMeFilter" class="org.springframework.security.web.authentication.rememberme.RememberMeAuthenticationFilter"
    p:rememberMeServices-ref="rememberMeServices"
    p:authenticationManager-ref="authenticationManager" />

    <bean id="rememberMeServices" class="org.springframework.security.web.authentication.rememberme.TokenBasedRememberMeServices"
    p:userDetailsService-ref="userManager"
    p:key="springRocks" />

    <bean id="rememberMeAuthenticationProvider" class="org.springframework.security.authentication.RememberMeAuthenticationProvider"
    p:key="springRocks" />

    <bean id="logoutFilter" class="org.springframework.security.web.authentication.logout.LogoutFilter"
    p:filterProcessesUrl="/auth/logout">
    <constructor-arg value="/auth/login" />
    <constructor-arg>
    <list>
    <ref bean="rememberMeServices" />
    <bean class="org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler" />
    </list>
    </constructor-arg>
    </bean>

    ReplyDelete
  13. @aron, thanks for the feedback and sharing your configuration. I'm sorry if I wasn't able to respond promptly.

    ReplyDelete
  14. I couldn't find all the information in sessionRegistry.getAllPrincipals() except only the username when using customAuthenticationManager rather than authenticationManager. Need a help about it.

    Thanks in advance.

    ReplyDelete
  15. the reason you don't find that information in sessionRegistry is that you return in method authenticate (in CustomAuthenticationManager):

    .....

    return new UsernamePasswordAuthenticationToken(
    auth.getName(),
    auth.getCredentials(),
    getAuthorities(user.getAccess()));

    ....

    You pass username as principal from auth.getName(), instead you could pass a UserDetails implementation so you can get user information from sessionRegistry and not just the username.

    ReplyDelete
  16. This is very interesting and give great information on Spring security. I am trying to create user/pwd to DB, i used MD5 encryption for pwd, but when I login using spring security defined in this blog i am getting You have entered an invalid username or password!.but I am able to login using other users john/jane as defined in this blog. Am i doing something wrong
    MessageDigest md;
    md = MessageDigest.getInstance("MD5");
    byte[] passBytes = pass.getBytes();
    md.reset();
    byte[] digested = md.digest(passBytes);
    StringBuffer sb = new StringBuffer();
    for(int i=0;i<digested.length;i++){
    sb.append(Integer.toHexString(0xff & digested[i]));
    }

    ReplyDelete
  17. Its all is too complicated....

    ReplyDelete
  18. This comment has been removed by the author.

    ReplyDelete
  19. session registry also returns recently logged out users in getAllPrincipals().

    ReplyDelete
  20. session registry also returns recently logged out users in getAllPrincipals.

    ReplyDelete
  21. getAllPrincipals returing a empty list please suggest

    ReplyDelete