nomusicnolife
nomusicnolife

Reputation: 81

Spring Security 3.1.0 - Remember-me does not work as expected

I am having a problem with Spring Security's 3.1.0 remember-me cookies. I need to find a solution ASAP and I cannot find the cause of this problem.

These are the steps I am following:

At this point I expect to enter my app bypassing the login form since the remember-me cookie is still in my browser. Despite that fact, the login page appears again.

Trying again, I can enter my app normally bypassing the login form.

This is quite confusing, and I cannot find an explanation so far.

I tried to debug both attempts and I found the following when Spring Security's RememberMeAuthenticationFilter was fired:

Attempt 1
request path = /app.htm: request.getCookies() did not contain my SPRING_SECURITY_REMEMBER_ME_COOKIE, therefore I was redirected to login page request path = /security/login.htm: at this point request.getCookies() DID have my SPRING_SECURITY_REMEMBER_ME_COOKIE, and it was accepted; anyway, I had already been redirected to the login page

Attempt 2
The remember-me cookie had already been accepted, so I could enter my app without problem.

Below are the Spring Security XML configuration and the logs of both attempts.

Any help on this will be very much appreciated!


Spring Security configuration (I will omit everything about daoAuthenticationProvider and event listeners):

<sec:http auto-config="false" use-expressions="true" authentication-manager-ref="authenticationManager">
    <sec:custom-filter ref="sessionLocaleResolvingFilter" before="FORM_LOGIN_FILTER"/>

    <sec:intercept-url pattern="/security/*.htm" requires-channel="https" />
    <sec:intercept-url pattern="/retrieve-password/*.htm" requires-channel="https" />
    <sec:intercept-url pattern="/messagebroker/*" access="authenticated" requires-channel="http" />
    <sec:intercept-url pattern="/platform/*.htm"
        access="hasRole('limited') or (authenticated and !hasRole('role1') and !hasRole('role2'))"
        requires-channel="http" />
    <sec:intercept-url pattern="/app.htm" access="authenticated" requires-channel="http" />
    <sec:intercept-url pattern="/**" requires-channel="http" />
    <sec:form-login login-page="/security/login.htm" default-target-url="/app.htm"
        login-processing-url="/security/process-login.htm" authentication-failure-url="/security/login.htm?error=true" />
    <sec:logout logout-url="/security/logout.htm" delete-cookies="JSESSIONID,SPRING_SECURITY_REMEMBER_ME_COOKIE"
        logout-success-url="/security/logout-success.htm" invalidate-session="true"/>
    <sec:anonymous/>
    <sec:remember-me use-secure-cookie="true" key="myAppServices" 
        services-ref="ipTokenBasedRememberMeServicesBean" />
    <sec:session-management session-fixation-protection="none"/>
    <sec:access-denied-handler error-page="/denied-access.htm"/>
</sec:http>

<bean id="sessionLocaleResolvingFilter" class="com.myapp.spring.security.SessionLocaleResolvingFilter" />
<bean class="com.myapp.spring.security.IPTokenBasedRememberMeServices"
    id="ipTokenBasedRememberMeServicesBean">
    <constructor-arg value="myAppServices"/>
    <constructor-arg ref="myAppJdbcDaoImpl"/>
</bean>
<bean id="myAppPasswordEncoder" class="com.myapp.spring.security.MyAppPasswordEncoder" />
<bean id="authenticationManager"
    class="o.s.s.authentication.ProviderManager">
    <property name="providers">
        <list>
            <ref local="daoAuthenticationProvider" />
        </list>
    </property>
</bean>
<!-- Other beans... -->

Log for the first attempt (replaced org.springframework with o.s and org.springframework.security with o.s.s):

o.s.s.web.access.channel.ChannelProcessingFilter:134 - Request: FilterInvocation: URL: /app.htm?lang=en; ConfigAttributes: [REQUIRES_INSECURE_CHANNEL]
o.s.s.web.context.HttpSessionSecurityContextRepository:127 - No HttpSession currently exists
o.s.s.web.context.HttpSessionSecurityContextRepository:85 - No SecurityContext was available from the HttpSession: null. A new one will be created.
o.s.s.web.FilterChainProxy:318 - /app.htm?lang=en at position 9 of 11 in additional filter chain; firing Filter: 'AnonymousAuthenticationFilter'
o.s.s.web.authentication.AnonymousAuthenticationFilter:102 - Populated SecurityContextHolder with anonymous token: 'o.s.s.authentication.AnonymousAuthenticationToken@90550640: Principal: anonymousUser; Credentials: [PROTECTED]; Authenticated: true; Details: o.s.s.web.authentication.WebAuthenticationDetails@7798: RemoteIpAddress: fe80:0:0:0:ec09:25fb:3df4:323b; SessionId: 057E689401E69589BB7359F3E95B4A18; Granted Authorities: ROLE_ANONYMOUS'
o.s.s.web.FilterChainProxy:318 - /app.htm?lang=en at position 10 of 11 in additional filter chain; firing Filter: 'ExceptionTranslationFilter'
o.s.s.web.FilterChainProxy:318 - /app.htm?lang=en at position 11 of 11 in additional filter chain; firing Filter: 'FilterSecurityInterceptor'
o.s.s.web.util.AntPathRequestMatcher:103 - Checking match of request : '/app.htm'; against '/messagebroker/*'
o.s.s.web.util.AntPathRequestMatcher:103 - Checking match of request : '/app.htm'; against '/app.htm'
o.s.s.web.access.intercept.FilterSecurityInterceptor:193 - Secure object: FilterInvocation: URL: /app.htm?lang=en; Attributes: [authenticated]
o.s.s.web.access.intercept.FilterSecurityInterceptor:298 - Previously Authenticated: o.s.s.authentication.AnonymousAuthenticationToken@90550640: Principal: anonymousUser; Credentials: [PROTECTED]; Authenticated: true; Details: o.s.s.web.authentication.WebAuthenticationDetails@7798: RemoteIpAddress: fe80:0:0:0:ec09:25fb:3df4:323b; SessionId: 057E689401E69589BB7359F3E95B4A18; Granted Authorities: ROLE_ANONYMOUS
o.s.s.access.vote.AffirmativeBased:65 - Voter: o.s.s.web.access.expression.WebExpressionVoter@1ce3388, returned: -1
o.s.s.web.access.ExceptionTranslationFilter:165 - Access is denied (user is anonymous); redirecting to authentication entry point
o.s.s.access.AccessDeniedException: Access is denied
    at o.s.s.access.vote.AffirmativeBased.decide(AffirmativeBased.java:83)
    at o.s.s.access.intercept.AbstractSecurityInterceptor.beforeInvocation(AbstractSecurityInterceptor.java:205)
    at o.s.s.web.access.intercept.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:114)
    at o.s.s.web.access.intercept.FilterSecurityInterceptor.doFilter(FilterSecurityInterceptor.java:83)
    at o.s.s.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:323)
    at o.s.s.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:113)
    at o.s.s.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:323)
    (X more)

o.s.s.web.DefaultRedirectStrategy:36 - Redirecting to 'http://arbad67464/services/security/login.htm'
o.s.s.web.context.HttpSessionSecurityContextRepository:269 - SecurityContext is empty or contents are anonymous - context will not be stored in HttpSession.
o.s.s.web.context.SecurityContextPersistenceFilter:97 - SecurityContextHolder now cleared, as request processing completed

o.s.s.web.access.channel.RetryWithHttpsEntryPoint:55 - Redirecting to: https://arbad67464/services/security/login.htm
o.s.s.web.DefaultRedirectStrategy:36 - Redirecting to 'https://arbad67464/services/security/login.htm'

o.s.s.web.access.channel.ChannelProcessingFilter:134 - Request: FilterInvocation: URL: /security/login.htm; ConfigAttributes: [REQUIRES_SECURE_CHANNEL]
o.s.s.web.FilterChainProxy:318 - /security/login.htm at position 2 of 11 in additional filter chain; firing Filter: 'SecurityContextPersistenceFilter'
o.s.s.web.context.HttpSessionSecurityContextRepository:139 - HttpSession returned null object for SPRING_SECURITY_CONTEXT
o.s.s.web.context.HttpSessionSecurityContextRepository:85 - No SecurityContext was available from the HttpSession: org.apache.catalina.session.StandardSessionFacade@16de87. A new one will be created.
o.s.s.web.FilterChainProxy:318 - /security/login.htm at position 3 of 11 in additional filter chain; firing Filter: 'LogoutFilter'
o.s.s.web.FilterChainProxy:318 - /security/login.htm at position 4 of 11 in additional filter chain; firing Filter: 'SessionLocaleResolvingFilter'
o.s.s.web.FilterChainProxy:318 - /security/login.htm at position 5 of 11 in additional filter chain; firing Filter: 'UsernamePasswordAuthenticationFilter'
o.s.s.web.FilterChainProxy:318 - /security/login.htm at position 6 of 11 in additional filter chain; firing Filter: 'RequestCacheAwareFilter'

o.s.s.web.FilterChainProxy:318 - /security/login.htm at position 7 of 11 in additional filter chain; firing Filter: 'SecurityContextHolderAwareRequestFilter'
o.s.s.web.FilterChainProxy:318 - /security/login.htm at position 8 of 11 in additional filter chain; firing Filter: 'RememberMeAuthenticationFilter'
com.myapp.spring.security.IPTokenBasedRememberMeServices:103 - Remember-me cookie detected
com.myapp.spring.security.IPTokenBasedRememberMeServices:118 - Remember-me cookie accepted
o.s.s.authentication.ProviderManager:152 - Authentication attempt using o.s.s.authentication.RememberMeAuthenticationProvider

Log of my second attempt:

o.s.s.web.access.channel.ChannelProcessingFilter:134 - Request: FilterInvocation: URL: /app.htm?lang=en; ConfigAttributes: [REQUIRES_INSECURE_CHANNEL]
o.s.s.web.context.HttpSessionSecurityContextRepository:158 - Obtained a valid SecurityContext from SPRING_SECURITY_CONTEXT: 'o.s.s.core.context.SecurityContextImpl@7db9504: Authentication: o.s.s.authentication.RememberMeAuthenticationToken@7db9504: Principal: com.myapp.spring.security.MyAppUserImpl@9716c62c: Username: [email protected]; Password: [PROTECTED]; Enabled: true; AccountNonExpired: true; credentialsNonExpired: true; AccountNonLocked: true; Granted Authorities: limited,premium,special; Credentials: [PROTECTED]; Authenticated: true; Details: o.s.s.web.authentication.WebAuthenticationDetails@7798: RemoteIpAddress: fe80:0:0:0:ec09:25fb:3df4:323b; SessionId: 057E689401E69589BB7359F3E95B4A18; Granted Authorities: limited, premium, special'
o.s.s.web.FilterChainProxy:318 - /app.htm?lang=en at position 8 of 11 in additional filter chain; firing Filter: 'RememberMeAuthenticationFilter'
o.s.s.web.authentication.rememberme.RememberMeAuthenticationFilter:142 - SecurityContextHolder not populated with remember-me token, as it already contained: 'o.s.s.authentication.RememberMeAuthenticationToken@7db9504: Principal: com.myapp.spring.security.MyAppUserImpl@9716c62c: Username: [email protected]; Password: [PROTECTED]; Enabled: true; AccountNonExpired: true; credentialsNonExpired: true; AccountNonLocked: true; Granted Authorities: limited,premium,special; Credentials: [PROTECTED]; Authenticated: true; Details: o.s.s.web.authentication.WebAuthenticationDetails@7798: RemoteIpAddress: fe80:0:0:0:ec09:25fb:3df4:323b; SessionId: 057E689401E69589BB7359F3E95B4A18; Granted Authorities: limited, premium, special'
o.s.s.web.FilterChainProxy:318 - /app.htm?lang=en at position 9 of 11 in additional filter chain; firing Filter: 'AnonymousAuthenticationFilter'
o.s.s.web.authentication.AnonymousAuthenticationFilter:107 - SecurityContextHolder not populated with anonymous token, as it already contained: 'o.s.s.authentication.RememberMeAuthenticationToken@7db9504: Principal: com.myapp.spring.security.MyAppUserImpl@9716c62c: Username: [email protected]; Password: [PROTECTED]; Enabled: true; AccountNonExpired: true; credentialsNonExpired: true; AccountNonLocked: true; Granted Authorities: limited,premium,special; Credentials: [PROTECTED]; Authenticated: true; Details: o.s.s.web.authentication.WebAuthenticationDetails@7798: RemoteIpAddress: fe80:0:0:0:ec09:25fb:3df4:323b; SessionId: 057E689401E69589BB7359F3E95B4A18; Granted Authorities: limited, premium, special'
o.s.s.web.access.intercept.FilterSecurityInterceptor:193 - Secure object: FilterInvocation: URL: /app.htm?lang=en; Attributes: [authenticated]
o.s.s.web.access.intercept.FilterSecurityInterceptor:298 - Previously Authenticated: o.s.s.authentication.RememberMeAuthenticationToken@7db9504: Principal: com.myapp.spring.security.MyAppUserImpl@9716c62c: Username: [email protected]; Password: [PROTECTED]; Enabled: true; AccountNonExpired: true; credentialsNonExpired: true; AccountNonLocked: true; Granted Authorities: limited,premium,special; Credentials: [PROTECTED]; Authenticated: true; Details: o.s.s.web.authentication.WebAuthenticationDetails@7798: RemoteIpAddress: fe80:0:0:0:ec09:25fb:3df4:323b; SessionId: 057E689401E69589BB7359F3E95B4A18; Granted Authorities: limited, premium, special
o.s.s.access.vote.AffirmativeBased:65 - Voter: o.s.s.web.access.expression.WebExpressionVoter@1ce3388, returned: 1
o.s.s.web.access.intercept.FilterSecurityInterceptor:214 - Authorization successful

Upvotes: 3

Views: 7866

Answers (2)

Vishnu
Vishnu

Reputation: 61

In the Spring security they provide 2 ways to use the rememberMe service.

  1. In the rememberMeService Definition, set a property alwaysRememberMe is true. In this case whenever first time user try to access the secure URL it will take to login page. once user logged in with correct user name and password, after onwards it wil not ask you for login until you logout.

  2. In the login page add a remember me checkbox with name "_spring_security_remember_me" and value="true". In this case when user select the remember me check box then only it will able to access to secure URL without login page until u logout.

Its working for me..

Upvotes: 5

Shaun the Sheep
Shaun the Sheep

Reputation: 22742

I'd guess it has something to do with the fact that you have some URLs configured to use HTTPS.

The remember-me cookie will have been marked as secure (you should be able to see this in the set-cookie header which your browser receives and probably also in the browser cookie cache). Since the request to /app is over HTTP, the cookie will not be sent. However, the request to the login page is redirected to HTTPS at which point the cookie will be sent.

You should really use HTTPS throughout. It is simpler to configure and your app isn't really secure otherwise. However, there is also a use-secure-cookie option in the remember-me namespace element, which you can set to false to override the default behaviour.

Upvotes: 3

Related Questions