3
votes

I'm new to Spring Security. I have a requirement to do a URL based authentication wherein a user needs to be authenticated based on a unique reference that will be sent every time as a parameter in the URL. I'll pass on this reference to a webservice, get the required data and then authenticate the user (and set the roles). The Authentication & Authorization part is working fine.

However, when i try to access the application again (now with a different reference in the URL), it says "SecurityContextHolder not populated with anonymous token, as it already contained ..." and it showed the details of the previous request. I've tried clearing the security context using SecurityContextHolder.getContext().setAuthentication(null) and SecurityContextHolder.clearContext().

After this, i was able to access the application multiple times. However, if i try to access the application simultaneously from my colleagues machine, i get a blank page. Upon checking the logs, i see a message "SecurityContextHolder not populated with anonymous token ...". I've also tried setting up the sessions but I'm clueless as to where I'm losing the track.

Below is my web.xml (only the spring security part):

<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>
          org.springframework.security.web.session.HttpSessionEventPublisher
    </listener-class>
</listener>

<session-config>
<session-timeout>30</session-timeout>
</session-config>

spring-security.xml:

<http use-expressions="true" auto-config="false" entry-point-                   
                            ref="http403ForbiddenEntryPoint">
<intercept-url pattern="/paymentPortalInternal.*" access="hasRole('Internal')"/>
<intercept-url pattern="/paymentPortalExternal.*" access="hasRole('External')"/>

<custom-filter position="PRE_AUTH_FILTER" ref="customAuthenticationFilter"/>
<custom-filter position="CONCURRENT_SESSION_FILTER" ref="concurrencyFilter" />
<session-management session-authentication-strategy-ref="sas"/>
</http>

<beans:bean id="concurrencyFilter"
class="org.springframework.security.web.session.ConcurrentSessionFilter">
<beans:property name="sessionRegistry" ref="sessionRegistry" />
<beans:property name="expiredUrl" value="/session-expired.htm" />
</beans:bean>

<beans:bean id="http403ForbiddenEntryPoint"
  class="org.springframework.security.web.authentication.Http403ForbiddenEntryPoint"/>
<beans:bean id="customAuthenticationFilter"
  class="com.xxx.xxx.xxxxx.security.CustomAuthenticationFilter">
      <beans:property name="sessionAuthenticationStrategy" ref="sas" />
  <beans:property name="authenticationManager" ref="authenticationManager" />
</beans:bean>

<beans:bean id="sas" 
              class="org.springframework.security.web.authentication.session.
                                               ConcurrentSessionControlStrategy">
<beans:constructor-arg name="sessionRegistry" ref="sessionRegistry" />
<beans:property name="maximumSessions" value="1" />
<beans:property name="exceptionIfMaximumExceeded" value="true" />
</beans:bean>

<beans:bean id="sessionRegistry"    
class="org.springframework.security.core.session.SessionRegistryImpl" />

<authentication-manager alias="authenticationManager">
    <authentication-provider ref="preauthAuthProvider" />
</authentication-manager>

<beans:bean id="preauthAuthProvider" 
                class="org.springframework.security.web.authentication.preauth.
                                       PreAuthenticatedAuthenticationProvider">
<beans:property name="preAuthenticatedUserDetailsService">
    <beans:bean class="com.XXX.XXXX.XXX.UserServiceImpl" />
    </beans:property>
</beans:bean>

Please let me know if I need to provide more information.

EDIT: Adding Logs.

For the first Request:

2013-02-07 17:27:38,834 DEBUG [http-8081-2][org.springframework.security.web.context.HttpSessionSecurityContextRepository.readSecurityContextFromSession(127)] - No HttpSession currently exists 2013-02-07 17:27:38,834 DEBUG [http-8081-2][org.springframework.security.web.context.HttpSessionSecurityContextRepository.loadContext(85)] - No SecurityContext was available from the HttpSession: null. A new one will be created.

For the second request(Please note that the details in the security context are those of the first request):

2013-02-07 17:27:54,272 DEBUG [http-8081-2][org.springframework.security.web.context.HttpSessionSecurityContextRepository.readSecurityContextFromSession(158)] - Obtained a valid SecurityContext from SPRING_SECURITY_CONTEXT: 'org.springframework.security.core.context.SecurityContextImpl@1017293c: Authentication: org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken@1017293c: Principal: org.springframework.security.core.userdetails.User@35cd3811: Username: Internal@5581e6e1-7e61-41bb-9257-b3c1acb96519; Password: [PROTECTED]; Enabled: true; AccountNonExpired: true; credentialsNonExpired: true; AccountNonLocked: true; Granted Authorities: Internal; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails@ffffc434: RemoteIpAddress: 10.188.182.107; SessionId: null; Granted Authorities: Internal'

My understanding is that security context holder stores the details of all the users. But in this case, I'm not able to launch the application from a different tab/browser.

2
It sounds like your custom authentication filter is ignoring the change in the URL parameter, perhaps because the user is already authenticated. Also, you should probably remove all the concurrent session stuff until you can get he basics working properly. - Shaun the Sheep
Thanks for your response Luke. The problem here is, when the second request is fired, my customAuthenticationFilter isn't getting triggered at all. Please see the log trace i've added to the question. - Mahesh Kumar
@LukeTaylor, Just to add one more point here.. We don't have a proper login mechanism in place. All we have is a unique reference which comes from the URL. So ideally when a second request comes, it is as good as a new user trying to get authenticated. However, the security context is already populated with the token of the first user and i'm clueless at the moment. I'm now trying out the SecurityContextPersistenceFilter filter. Am i going in the right direction? - Mahesh Kumar
If you are connecting in the same session then it's no surprise that the security context already contains the previous authentication. You really should post the outline of your CustomAuthenticationFilter. You say it isn't "triggered". Have you debugged it to find out why? It's a filter, hence its doFilter method is called for every request. Put a breakpoint there. And you haven't clarified why you are using all the concurrent session infrastructure. Also, passing authentication parameters in a URL is generally frowned on for security reasons. - Shaun the Sheep
@LukeTaylor, I found out the reason why my customAuthFilter isn't getting triggered for the second request. It appears that the position that I'm invoking it from is wrong. I used position="PRE_AUTH_FILTER" earlier. I've tried with other positions (like FIRST, BASIC_AUTH_FILTER, etc) and the filter is able to authenticate multiple requests. However, the anonymous filter is getting triggered after my customAuthFilter and it is overriding the security context with an anonymous token. Is there any way to avoid this? I'm now trying to use the filterProxyChain to use only the required filters. - Mahesh Kumar

2 Answers

1
votes

If your CustomAuthenticationFilter is extending AbstractPreAuthenticatedProcessingFilter, following 2 properties may give you an insight. 1. checkForPrincipalChanges 2. invalidateSessionOnPrincipalChange

1
votes

I was able to fix this issue by overriding the default filter chain (used filter chain proxy) and invoking only the required filters. Thanks LukeTaylor and Ketan for your suggestions. Please let me know if some one has the same issue. I can post my XML and other stuff.