Announcement Announcement Module
Collapse
No announcement yet.
Cluster-aware SessionRegistry for concurrent logins? Page Title Module
Move Remove Collapse
This topic is closed
X
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • Cluster-aware SessionRegistry for concurrent logins?

    I tried to replace Spring Security 2.0.2's default SessionRegistry with a custom SessionRegistry implementation which uses either EHCache or a JGroups' ReplicatedHashMap to store the principals and sessionIds. I tried both approaches, but neither of them makes the concurrent login filter work as expected (ie. allow only one concurrent login for each user regardless their used cluster node).

    I can post my modified SessionRegistry code but I doubt the problem is there (it's a plain replacement of Maps to Caches or ReplicatedHashMaps, nothing else). Any clue what could be wrong with such a setup?

    Generally, is it a naive theory to handle concurrent logins in a cluster this way?

  • #2
    Any hints, please?

    Comment


    • #3
      I don't know about this approach but it has certainly been done with a database before.

      Comment


      • #4
        Thanks Luke!

        The main point of the question was that simply replacing SessionRegistry alone could automatically making it work in a cluster or you think I need something else as well?

        Comment


        • #5
          Any news on this one?

          The problem is, that the SessionRegistryImpl that comes with spring security only is aware of the sessions created on the current node. if one clusters an application by using a HttpSession stored in a central database, the concurrent session support in spring security fails.

          ie. what is the best practice for having a cluster aware SessionRegistry.

          dyn: i'd love to see your code and hear of any progress you may have made.

          thanks.

          Comment


          • #6
            I am also having this issue. Has any solved this before?

            Comment


            • #7
              What application server are you using? Also, what problem are you trying to solve by using the session registry?

              Comment


              • #8
                I am using Oracle Application Server. The application is using the ConcurrentSessionFilter to disable concurrent logons. This worked fine until the application was put in front of a clustering/load balancing environment. Now the same user can login many times.

                Comment


                • #9
                  We have recently implemented a solution that uses GigaSpaces to store session information so that other applications in the cluster are aware of what sessions are in use.

                  I think using either a clustered cache or database is the best option.

                  Comment


                  • #10
                    Originally posted by hoffmandirt View Post
                    I am using Oracle Application Server. The application is using the ConcurrentSessionFilter to disable concurrent logons. This worked fine until the application was put in front of a clustering/load balancing environment. Now the same user can login many times.
                    This works fine with a custom SessionRegistryImpl which uses Hibernate with Oracle db, which has two clustered tomcat instances behind a load balancer @ Colombo Stock Exchange website.

                    Comment


                    • #11
                      How do you ensure that the table storing the session information gets cleaned out? Consider the following example. There are two servers, server A and server B, which are behind a load balancer. A custom SessionRegistryImpl is being used that stores the information in a database. The plug gets pulled to server A, so server B takes over and now has the session information. Server A comes back online. I'm worried that when server A comes back up that it will wipe out all of the session information, assuming I tie into application events. I also don't want unused sessions being left in the database.

                      Comment


                      • #12
                        In our database table we have the LAST_REQUEST. So whenever someone tries to log in, if the session info is there and the LAST_REQUEST is older than the maximum validity period specified, that info is automatically removed from the table. So there will be no sessions hooking up in the db even if any server goes down.

                        When any of the servers comes back online it never lead to wipe out all sessions. Remember these are only Sessions related to Spring Security. Even there are no registered HTTP sessions with the server, it does not clear Session Registry entries in Spring.

                        We are using tomcat 6 with oracle 9i.

                        We went to production about 6 months back. There are about 1100 registered users. Still we do not have issues with this clustered spring security authentication module.

                        Comment


                        • #13
                          I got this working rather well, but I have one more issue to work out. When I gracefully shut down one of the web server intances(Oracle Application Server) in the cluster, the session information gets removed from the database because an HttpSessionDestroyedEvent is fired off, but the principal stored in the authentication object is still there, meaning the user is still authenticated even though no data exists for that user in the database. I was going to remove the listener for this, but I noticed that the event is fired off when the user logs out as well. How did you get around this? The ConcurrentSessionFilter allows a null SessionInformation to pass.

                          Here is my event handler:

                          Code:
                          public void onApplicationEvent(ApplicationEvent event) {
                              if (event instanceof HttpSessionDestroyedEvent) {
                                  String sessionId = ((HttpSession) event.getSource()).getId();
                                  this.removeSessionInformation(sessionId); // Deletes from database
                              }
                          }
                          The doFilter method from Spring Security's ConcurrentSessonFilter (See my comment near the null check):

                          Code:
                          public void doFilterHttp(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
                                  throws IOException, ServletException {
                          
                              HttpSession session = request.getSession(false);
                          
                              if (session != null) {
                                  SessionInformation info = sessionRegistry.getSessionInformation(session.getId());
                          
                                  if (info != null) { //**** THE INFO IS NULL HERE SO THE USER PASSES AUTHENTICATION
                                      if (info.isExpired()) {
                                          // Expired - abort processing
                                          doLogout(request, response);
                          
                                          String targetUrl = determineExpiredUrl(request, info);
                          
                                          if (targetUrl != null) {
                                              targetUrl = request.getContextPath() + targetUrl;
                                              response.sendRedirect(response.encodeRedirectURL(targetUrl));
                                          } else {
                                              response.getWriter().print("This session has been expired (possibly due to multiple concurrent " +
                                                      "logins being attempted as the same user).");
                                              response.flushBuffer();
                                          }
                          
                                          return;
                                      } else {
                                          // Non-expired - update last request date/time
                                          info.refreshLastRequest();
                                      }
                                  }
                              }
                          
                              chain.doFilter(request, response);
                          }

                          Comment


                          • #14
                            What about relying on the Application Server to do this?

                            Most Application Servers have High Availability DataStores where they store HttpSessions and Single Sign On state.

                            I think the concurrent sign on protection is pretty buggy...I couldn't even get it to work properly with one server (Glassfish based AND Tomcat based).

                            How did the others get this to work? I am using Spring Security 2.0.4.

                            Comment


                            • #15
                              I am also using Spring Security 2.0.4 and this works well with one server. Here is how I did it:

                              1. Add the needed beans:

                              Code:
                                  <bean id="sessionRegistry" class="org.springframework.security.concurrent.SessionRegistryImpl"/>
                                  
                                  <bean id="concurrentSessionFilter" class="org.springframework.security.concurrent.ConcurrentSessionFilter">
                                      <property name="expiredUrl" value="/login.htm"/>
                                      <property name="sessionRegistry" ref="sessionRegistry"/>
                                  </bean>    
                              	
                              	<bean id="concurrentSessionController" class="org.springframework.security.concurrent.ConcurrentSessionControllerImpl">
                              	  <property name="maximumSessions" value="1"/>
                              	  <property name="sessionRegistry" ref="sessionRegistry" />
                                    <property name="exceptionIfMaximumExceeded" value="true"/>	  
                              	</bean>
                              2. Add the controller to the authentication manager:

                              Code:
                                  <bean id="authenticationManager" class="org.springframework.security.providers.ProviderManager">
                                      <property name="providers">
                                          <list>
                                              <ref local="customDaoAuthenticationProvider"/>
                                              <bean class="org.springframework.security.providers.anonymous.AnonymousAuthenticationProvider">
                                                  <property name="key" value="changeThis"/>
                                              </bean>
                                              <bean class="org.springframework.security.providers.rememberme.RememberMeAuthenticationProvider">
                                                  <property name="key" value="changeThis"/>
                                              </bean>
                                          </list>
                                      </property>
                                      <property name="sessionController" ref="concurrentSessionController"/>
                                  </bean>
                              3. Add the filter to the filter chain:

                              Code:
                                  <bean id="springSecurityFilterChain" class="org.springframework.security.util.FilterChainProxy">
                                      <property name="filterInvocationDefinitionSource">
                                          <value><![CDATA[
                                              CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON
                                              PATTERN_TYPE_APACHE_ANT
                              /**=concurrentSessionFilter,httpSessionContextIntegrationFilter,logoutFilter,authenticationProcessingFilter,securityContextHolderAwareRequestFilter,rememberMeProcessingFilter,anonymousProcessingFilter,exceptionTranslationFilter,filterInvocationInterceptor
                                          ]]></value>
                                      </property>
                                  </bean>
                              4. Add the session event publisher to web.xml

                              Code:
                                  <listener>
                                      <listener-class>org.springframework.security.ui.session.HttpSessionEventPublisher</listener-class>
                                  </listener>

                              Comment

                              Working...
                              X