I have tried to limit my concurrent users in my spring app.
I am using CAS 3.5 & spring security 3.1.0
I have succeeded to configure concurrent session management with CONCURRENT_SESSION_FILTER without CAS. But with CAS, no success. I thought I could replace FORM_LOGIN_FILTER with CAS_FILTER such like that: (omitted some of the config)
Code:
<security:http entry-point-ref="casEntryPoint" use-expressions="true">
<security:intercept-url pattern="/" access="permitAll"/>
<security:intercept-url pattern="/param" access="hasRole('ROLE_USER')"/>
<security:custom-filter position="CONCURRENT_SESSION_FILTER" ref="concurrencyFilter"/>
<security:custom-filter position="CAS_FILTER" ref="casFilter" />
<security:session-management session-authentication-strategy-ref="sas" />
<!-- for single logout -->
<security:logout logout-success-url="/cas-logout.jsp"/>
<security:custom-filter ref="requestSingleLogoutFilter" before="LOGOUT_FILTER"/>
<security:custom-filter ref="singleLogoutFilter" before="CAS_FILTER"/>
</security:http>
<bean id="casFilter" class="org.springframework.security.cas.web.CasAuthenticationFilter">
<property name="sessionAuthenticationStrategy" ref="sas"></property>
<property name="authenticationManager" ref="authenticationManager"/>
</bean>
<bean id="concurrencyFilter" class="org.springframework.security.web.session.ConcurrentSessionFilter">
<property name="sessionRegistry" ref="sessionRegistry" />
<property name="expiredUrl" value="/session-expired.htm" />
</bean>
<bean id="sas" class="org.springframework.security.web.authentication.session.ConcurrentSessionControlStrategy">
<constructor-arg name="sessionRegistry" ref="sessionRegistry" />
<property name="maximumSessions" value="1" />
</bean>
<bean id="sessionRegistry" class="org.springframework.security.core.session.SessionRegistryImpl" />
And then it lead to an infinite redirect loop on authentication with cas server.
I traced the log file and figured what was happening
Code:
DEBUG: org.springframework.security.authentication.ProviderManager - Authentication attempt using org.springframework.security.cas.authentication.CasAuthenticationProvider
DEBUG: org.springframework.security.cas.authentication.CasAuthenticationProvider - serviceUrl = https://localhost:8443/multisource/j_spring_cas_security_check
DEBUG: com.fm.multisource.security.CustomUserDetailsService - Detected authorities for user koala are:
DEBUG: com.fm.multisource.security.CustomUserDetailsService - ROLE_USER
DEBUG: org.springframework.security.web.authentication.session.ConcurrentSessionControlStrategy - Invalidating session with Id '8E8F210E2138C80502AEAC7EE6303FA7' and migrating attributes.
DEBUG: org.springframework.security.web.session.HttpSessionEventPublisher - Publishing event: org.springframework.security.web.session.HttpSessionDestroyedEvent[source=org.apache.catalina.session.StandardSessionFacade@139d891]
DEBUG: org.springframework.security.web.session.HttpSessionEventPublisher - Publishing event: org.springframework.security.web.session.HttpSessionCreatedEvent[source=org.apache.catalina.session.StandardSessionFacade@1a6ac39]
DEBUG: org.springframework.security.web.authentication.session.ConcurrentSessionControlStrategy - Started new session: 9571DCAC11C4C684C5239E3323304FEE
DEBUG: org.springframework.security.core.session.SessionRegistryImpl - Registering session 9571DCAC11C4C684C5239E3323304FEE, for principal com.fm.multisource.security.CustomUserDetails@f87478
DEBUG: org.springframework.security.core.session.SessionRegistryImpl - Registering session 9571DCAC11C4C684C5239E3323304FEE, for principal com.fm.multisource.security.CustomUserDetails@f87478
DEBUG: org.springframework.security.core.session.SessionRegistryImpl - Removing session 9571DCAC11C4C684C5239E3323304FEE from principal's set of registered sessions
DEBUG: org.springframework.security.core.session.SessionRegistryImpl - Removing principal com.fm.multisource.security.CustomUserDetails@f87478 from registry
DEBUG: org.springframework.security.cas.web.CasAuthenticationFilter - serviceTicketRequest = true
DEBUG: org.springframework.security.cas.web.CasAuthenticationFilter - Authentication success. Updating SecurityContextHolder to contain: org.springframework.security.cas.authentication.CasAuthenticationToken@e0f3e7e7: Principal: com.fm.multisource.security.CustomUserDetails@f87478; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails@166c8: RemoteIpAddress: 0:0:0:0:0:0:0:1; SessionId: 8E8F210E2138C80502AEAC7EE6303FA7; Granted Authorities: ROLE_USER Assertion: org.jasig.cas.client.validation.AssertionImpl@174f6ef Credentials (Service/Proxy Ticket): ST-92-EiLGxlhtBnvqNAOGfNny-cas01.example.org
DEBUG: org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler - Redirecting to DefaultSavedRequest Url: http://localhost:8080/multisource/param
DEBUG: org.springframework.security.web.DefaultRedirectStrategy - Redirecting to 'http://localhost:8080/multisource/param'
DEBUG: org.springframework.security.web.context.HttpSessionSecurityContextRepository - SecurityContext stored to HttpSession:8E8F210E2138C80502AEAC7EE6303FA7 'org.springframework.security.core.context.SecurityContextImpl@e0f3e7e7: Authentication: org.springframework.security.cas.authentication.CasAuthenticationToken@e0f3e7e7: Principal: com.fm.multisource.security.CustomUserDetails@f87478; Credentials: [PROTECTED]; Authenticated: true; Details: SessionId: ];org.springframework.security.web.authentication.WebAuthenticationDetails@166c8: RemoteIpAddress: 0:0:0:0:0:0:0:1; Granted Authorities: ROLE_USER Assertion: org.jasig.cas.client.validation.AssertionImpl@174f6ef Credentials (Service/Proxy Ticket): ST-92-EiLGxlhtBnvqNAOGfNny-cas01.example.org'
DEBUG: org.springframework.security.web.context.SecurityContextPersistenceFilter - SecurityContextHolder now cleared, as request processing completed
If you look carefully(look at the bolded texts from the log):
- CasAuthenticationProvider authenticates the user
- ConcurrentSessionControlStrategy creates new session, copies details from the current and invalidates the current session
- CasAuthenticationFilter registers with an invalidated session
- Because of using invalidated session, it would loop back to the step 1 and repeats infinitely
Am I missing something? Is it a bug? Or is CAS just not meant to work with ConcurrentSessionFilter?