I have two sets of resource APIs, each of which must be validated against distinct remote authorization servers.
Below is my security-config.xml
<?xml version="1.0" encoding="UTF-8"?>
<b:beans xmlns="http://www.springframework.org/schema/security"
xmlns:b="http://www.springframework.org/schema/beans"
xmlns:oauth="http://www.springframework.org/schema/security/oauth2"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-4.2.xsd
http://www.springframework.org/schema/security/oauth2 http://www.springframework.org/schema/security/spring-security-oauth2-2.0.xsd">
<authentication-manager>
</authentication-manager>
<oauth:resource-server id="resourceFilterA"
token-services-ref="tokenServicesA"/>
<http pattern="/v1/A/**"
request-matcher="ant"
disable-url-rewriting="true"
use-expressions="true"
entry-point-ref="entryPointA">
<intercept-url pattern="/v1/A/endpoint1"
method="GET"
access="hasAuthority('scope.A')"/>
<anonymous enabled="true"/>
<csrf disabled="true"/>
<custom-filter ref="resourceFilterA" position="PRE_AUTH_FILTER"/>
</http>
<oauth:resource-server id="resourceFilterB"
token-services-ref="tokenServicesB"/>
<http pattern="/v1/B/**"
request-matcher="ant"
disable-url-rewriting="true"
use-expressions="true"
entry-point-ref="entryPointB">
<intercept-url pattern="/v1/B/endpoint1"
method="POST"
access="hasAuthority('scope.B')"/>
<anonymous enabled="true"/>
<csrf disabled="true"/>
<custom-filter ref="resourceFilterB" position="PRE_AUTH_FILTER"/>
</http>
</b:beans>
The tokenServicesA and tokenServicesB are defined in Java config:
#Bean
public OAuth2AuthenticationEntryPoint entryPointA() {
return new OAuth2AuthenticationEntryPoint();
}
#Bean
public OAuth2AuthenticationEntryPoint entryPointB() {
return new OAuth2AuthenticationEntryPoint();
}
#Bean
public ResourceServerTokenServices tokenServicesA() {
FastRemoteTokenServices tokenServices = new FastRemoteTokenServices();
tokenServices.setUseHttps(true);
tokenServices.setStoreClaims(true);
tokenServices.setTrustedIssuerIdsRegex(
Pattern.compile(trustedIssuerA, Pattern.LITERAL).pattern());
return tokenServices;
}
#Bean
public ResourceServerTokenServices tokenServicesB() {
FastRemoteTokenServices tokenServices = new FastRemoteTokenServices();
tokenServices.setUseHttps(true);
tokenServices.setStoreClaims(true);
tokenServices.setTrustedIssuerIdsRegex(
Pattern.compile(trustedIssuerB, Pattern.LITERAL).pattern());
return tokenServices;
}
I expected exactly one of the filters to match the request based on the mutually exclusive intercept-url patterns in the http blocks.
However, it appears that for the sample request logged below which bears a token from trusted issuer A, while the first http tag does match and successfully authenticates the request, that the trusted issuer in the token is still checked against the trusted issuer B regexp, which fails.
Similarly, a request with a token from trusted issuer B is also matched against the regexp of trusted issuer A, which also fails.
estMatcher : Checking match of request : '/v1/A/endpoint1'; against '/v1/A/endpoint1'
2018-02-20 21:46:09.879 DEBUG 8439 --- [io-12525-exec-4] o.s.s.w.a.i.FilterSecurityInt
erceptor : Secure object: FilterInvocation: URL: /v1/A/endpoint1; Attribu
tes: [hasAuthority('scope.A')]
2018-02-20 21:46:09.879 DEBUG 8439 --- [io-12525-exec-4] o.s.s.w.a.i.FilterSecurityInt
erceptor : Previously Authenticated: org.springframework.security.oauth2.provider.O
Auth2Authentication#cc91d34f: Principal: apg-client; Credentials: [PROTECTED]; Authent
icated: true; Details: remoteAddress=0:0:0:0:0:0:0:1, tokenType=BearertokenValue=<TOKE
N>; Granted Authorities: scope.A
2018-02-20 21:46:10.110 DEBUG 8439 --- [io-12525-exec-4] o.s.s.access.vote.Affirmative
Based : Voter: org.springframework.security.web.access.expression.WebExpressionV
oter#a7694fd, returned: 1
2018-02-20 21:46:10.110 DEBUG 8439 --- [io-12525-exec-4] o.s.s.w.a.i.FilterSecurityInt
erceptor : Authorization successful
2018-02-20 21:46:10.110 DEBUG 8439 --- [io-12525-exec-4] o.s.s.w.a.i.FilterSecurityInt
erceptor : RunAsManager did not change Authentication object
2018-02-20 21:46:10.110 DEBUG 8439 --- [io-12525-exec-4] o.s.security.web.FilterChainP
roxy : /v1/A/endpoint1 reached end of additional filter chain; procee
ding with original chain
2018-02-20 21:46:10.111 DEBUG 8439 --- [io-12525-exec-4] p.a.OAuth2AuthenticationProce
ssingFilter : Authentication request failed: error="invalid_token", error_description=
"The issuer 'https://my.issuer.A/oauth/token' is not trusted because it does not match
the regex '<my-issuer-B-regexp>'
2018-02-20 21:46:10.112 DEBUG 8439 --- [io-12525-exec-4] w.c.HttpSessionSecurityContex
tRepository : SecurityContext is empty or contents are anonymous - context will not be
stored in HttpSession.
2018-02-20 21:46:10.113 DEBUG 8439 --- [io-12525-exec-4] s.s.o.p.e.DefaultOAuth2Except
ionRenderer : Written [error="invalid_token", error_description="The issuer 'https://m
y.issuer.A/oauth/token' is not trusted because it does not match the regex '<my-issuer
-B-regexp>' /json;charset=UTF-8" using [org.springframework.http.converter.json.Mappin
gJackson2Htt pMessageConverter#25743461]
2018-02-20 21:46:10.113 DEBUG 8439 --- [io-12525-exec-4] o.s.s.w.a.ExceptionTranslatio
nFilter : Chain processed normally
2018-02-20 21:46:10.113 DEBUG 8439 --- [io-12525-exec-4] s.s.w.c.SecurityContextPersis
tenceFilter : SecurityContextHolder now cleared, as request processing completed
2018-02-20 21:46:10.113 DEBUG 8439 --- [io-12525-exec-4] o.s.b.w.f.OrderedRequestConte
xtFilter : Cleared thread-bound request context: org.apache.catalina.connector.Requ
estFacade#74f7a53b
What is the fundamental mistake in the configuration above?
Alternatively, what is a good reference example with multiple oauth resource servers, each protected by a distinct FastRemoteTokenServices token service?
Related
App security scenario:
Spring MVC app running 3 instances on PaaS
App is split in to 2 security domains. Managed w/ 2 DispatchServlets located at /app and /kmlservice
App must use https on all pages except for any paths within /kmlservice
App must use custom login page to access all paths within /app
App must use persistent remember-me scheme with LDAP auth
When we converted to https, we are getting infinite redirect loops when attempting to navigate to the /app/login page or any other page. In fact even unprotected pages infinitely reroute to themselves.
Here is an example of the redirect logs we are seeing on attempting to navigate to /app/login:
stdout.log: DEBUG: org.springframework.security.web.access.channel.ChannelProcessingFilter - Request: FilterInvocation: URL: /app/login; ConfigAttributes: [REQUIRES_SECURE_CHANNEL]
stdout.log: DEBUG: org.springframework.security.web.access.channel.RetryWithHttpsEntryPoint - Redirecting to: https:/some_url.com/app/login
stdout.log: DEBUG: org.springframework.security.web.DefaultRedirectStrategy - Redirecting to 'https:/some_url.com/app/login'
stdout.log: DEBUG: org.springframework.security.web.util.matcher.AntPathRequestMatcher - Checking match of request : '/app/login'; against '/kmlservice/**'
stdout.log: DEBUG: org.springframework.security.web.util.matcher.AntPathRequestMatcher - Checking match of request : '/app/login'; against '/resources/**'
stdout.log: DEBUG: org.springframework.security.web.util.matcher.AntPathRequestMatcher - Checking match of request : '/app/login'; against '/app/**'
stdout.log: DEBUG: org.springframework.security.web.FilterChainProxy - /app/login at position 1 of 12 in additional filter chain; firing Filter: 'ChannelProcessingFilter'
stdout.log: DEBUG: org.springframework.security.web.util.matcher.AntPathRequestMatcher - Checking match of request : '/app/login'; against '/app/logout'
stdout.log: DEBUG: org.springframework.security.web.util.matcher.AntPathRequestMatcher - Checking match of request : '/app/login'; against '/app/accessdenied'
stdout.log: DEBUG: org.springframework.security.web.util.matcher.AntPathRequestMatcher - Checking match of request : '/app/login'; against '/app/useful_path'
stdout.log: DEBUG: org.springframework.security.web.util.matcher.AntPathRequestMatcher - Checking match of request : '/app/login'; against '/app/help'
stdout.log: DEBUG: org.springframework.security.web.util.matcher.AntPathRequestMatcher - Checking match of request : '/app/login'; against '/app/login'
stdout.log: DEBUG: org.springframework.security.web.access.channel.ChannelProcessingFilter - Request: FilterInvocation: URL: /app/login; ConfigAttributes: [REQUIRES_SECURE_CHANNEL]
stdout.log: DEBUG: org.springframework.security.web.access.channel.RetryWithHttpsEntryPoint - Redirecting to: https:/some_url.com/app/login
stdout.log: DEBUG: org.springframework.security.web.DefaultRedirectStrategy - Redirecting to 'https:/some_url.com/app/login'
Here's what the config looks like:
<http pattern="/kmlservice/**" use-expressions="true" auto-config="true">
<intercept-url pattern="/**" access="isAuthenticated()" />
<http-basic />
</http>
<http pattern="/resources/**" security="none" />
<http pattern="/app/**" use-expressions="true">
<form-login login-page="/app/login"
authentication-failure-url="/app/accessdenied" default-target-url="/app" />
<intercept-url pattern="/app/logout" access="permitAll"
requires-channel="https" />
<intercept-url pattern="/app/accessdenied" access="permitAll"
requires-channel="https" />
<intercept-url pattern="/app/useful_path"
access="hasRole('ROLE_HAS_ACCESS')" requires-channel="https" />
<intercept-url pattern="/app/help" access="permitAll"
requires-channel="https" />
<intercept-url pattern="/app/login" access="IS_AUTHENTICATED_ANONYMOUSLY"
requires-channel="https" />
<intercept-url pattern="/app/**" access="isAuthenticated()"
requires-channel="https" />
<access-denied-handler error-page="/403" />
<logout logout-success-url="/app/logout" delete-cookies="JSESSIONID" />
<remember-me user-service-ref="userDetailsService"
data-source-ref="dataSource" />
</http>
I have tried to remove
<intercept-url pattern="/app/**" access="isAuthenticated()" requires-channel="https" />
which seemed to make no difference
Is there any other useful config I could provide?
Thanks.
If https terminates at your router, as is often the case with PaaS configurations, then your servlet container needs some way to work out whether the incoming request was actually secure or not. Spring Security uses the standard servlet API method isSecure to decide whether it needs to redirect or not. I'd guess that in your case, the servlet container can't tell whether the external request to the router was made over HTTPS or not.
Tomcat, for example, can be configured with the RemoteIpValve to check for particular headers and set the request properties accordingly. I don't know whether you have any control over this, but you there's an equivalent filter which you can use instead. Of course this also requires that you know how your PaaS is setup and whether it forwards headers like X-Forwarded-Proto to your app at all.
It is going in indefinite loop because URL /app/login is secured as it is marked with IS_AUTHENTICATED_ANONYMOUSLY.
Change the access value to permitAll
<intercept-url pattern="/app/login" access="permitAll" requires-channel="https" />
Using Spring security 3.2, I am trying to intercept urls as follows:
<security:http use-expressions="true" path-type="regex" >
<security:intercept-url pattern="/jsp/Error_403.jsp" access="hasAnyRole('ROLE_VISITOR','ROLE_ADMIN','ROLE_BRONZE_SUB','ROLE_BRONZE_TEST','ROLE_SILVER_SUB','ROLE_SILVER_TEST','ROLE_GOLD_SUB','ROLE_GOLD_TEST')" />
<security:intercept-url pattern="/jsp/LoggedOut.jsp" access="hasAnyRole('ROLE_VISITOR','ROLE_ADMIN','ROLE_BRONZE_SUB','ROLE_BRONZE_TEST','ROLE_SILVER_SUB','ROLE_SILVER_TEST','ROLE_GOLD_SUB','ROLE_GOLD_TEST')" />
<security:intercept-url pattern="/jsp/home/header.html" access="hasAnyRole('ROLE_VISITOR','ROLE_ADMIN','ROLE_BRONZE_SUB','ROLE_BRONZE_TEST','ROLE_SILVER_SUB','ROLE_SILVER_TEST','ROLE_GOLD_SUB','ROLE_GOLD_TEST')"/>
<security:intercept-url pattern="/unjust?action=statistics" access="hasAnyRole('ROLE_ADMIN','ROLE_GOLD_SUB','ROLE_GOLD_TEST')" />
<security:intercept-url pattern="/unjust?action=browse" access="hasAnyRole('ROLE_ADMIN')" />
<security:intercept-url pattern="/unjust?action=search" access="hasAnyRole('ROLE_ADMIN','ROLE_BRONZE_SUB','ROLE_BRONZE_TEST','ROLE_SILVER_SUB','ROLE_SILVER_TEST','ROLE_GOLD_SUB','ROLE_GOLD_TEST')" />
<security:intercept-url pattern="/unjust?action=home" access="hasAnyRole('ROLE_ADMIN','ROLE_BRONZE_SUB','ROLE_BRONZE_TEST','ROLE_SILVER_SUB','ROLE_SILVER_TEST','ROLE_GOLD_SUB','ROLE_GOLD_TEST')" />
<security:intercept-url pattern="/unjust" access="hasAnyRole('ROLE_VISITOR','ROLE_ADMIN','ROLE_BRONZE_SUB','ROLE_BRONZE_TEST','ROLE_SILVER_SUB','ROLE_SILVER_TEST','ROLE_GOLD_SUB','ROLE_GOLD_TEST')"/>
<security:intercept-url pattern="/**" access="denyAll" />
<access-denied-handler error-page="/jsp/Error_403.jsp"/>
<security:logout />
<security:openid-login login-page="/openidlogin.jsp"
user-service-ref="registeringUserService" authentication-failure-url="/openidlogin.jsp?login_error=true">
<attribute-exchange identifier-match="https://www.google.com/.*">
<openid-attribute name="email"
type="http://axschema.org/contact/email" required="true" count="1" />
<openid-attribute name="firstname"
type="http://axschema.org/namePerson/first" required="true" />
<openid-attribute name="lastname"
type="http://axschema.org/namePerson/last" required="true" />
</attribute-exchange>
<attribute-exchange identifier-match=".*myopenid.com.*">
<openid-attribute name="email"
type="http://schema.openid.net/contact/email" required="true" />
<openid-attribute name="fullname"
type="http://schema.openid.net/namePerson" required="true" />
</attribute-exchange>
</security:openid-login>
<remember-me token-repository-ref="tokenRepo" />
</security:http>
When I try to execute, for example, the browse action, I cannot authenticate, and the log shows this:
12889 [http-bio-8080-exec-14] DEBUG org.springframework.security.web.FilterChainProxy - /unjust?action=statistics at position 11 of 11 in additional filter chain; firing Filter: 'FilterSecurityInterceptor'
12889 [http-bio-8080-exec-14] DEBUG org.springframework.security.web.util.RegexRequestMatcher - Checking match of request : '/unjust?action=statistics'; against '/jsp/Error_403.jsp'
12889 [http-bio-8080-exec-14] DEBUG org.springframework.security.web.util.RegexRequestMatcher - Checking match of request : '/unjust?action=statistics'; against '/jsp/LoggedOut.jsp'
12889 [http-bio-8080-exec-14] DEBUG org.springframework.security.web.util.RegexRequestMatcher - Checking match of request : '/unjust?action=statistics'; against '/jsp/home/header.html'
12889 [http-bio-8080-exec-14] DEBUG org.springframework.security.web.util.RegexRequestMatcher - Checking match of request : '/unjust?action=statistics'; against '/unjust?action=statistics'
12889 [http-bio-8080-exec-14] DEBUG org.springframework.security.web.util.RegexRequestMatcher - Checking match of request : '/unjust?action=statistics'; against '/unjust?action=browse'
12889 [http-bio-8080-exec-14] DEBUG org.springframework.security.web.util.RegexRequestMatcher - Checking match of request : '/unjust?action=statistics'; against '/unjust?action=search$'
12890 [http-bio-8080-exec-14] DEBUG org.springframework.security.web.util.RegexRequestMatcher - Checking match of request : '/unjust?action=statistics'; against '/unjust?action=home'
12890 [http-bio-8080-exec-14] DEBUG org.springframework.security.web.util.RegexRequestMatcher - Checking match of request : '/unjust?action=statistics'; against '/unjust'
12890 [http-bio-8080-exec-14] DEBUG org.springframework.security.web.access.intercept.FilterSecurityInterceptor - Secure object: FilterInvocation: URL: /unjust?action=statistics; Attributes: [denyAll]
12890 [http-bio-8080-exec-14] DEBUG org.springframework.security.web.access.intercept.FilterSecurityInterceptor - Previously Authenticated: [org.springframework.security.openid.OpenIDAuthenticationToken#c8f995e9: Principal: unjust.beans.CustomUserDetails#d66c4fbd: Username: https://www.google.com/accounts/o8/id?xxxxx; Password: [PROTECTED]; Enabled: true; AccountNonExpired: true; credentialsNonExpired: true; AccountNonLocked: true; Granted Authorities: ROLE_BRONZE_SUB; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails#380f4: RemoteIpAddress: 127.0.0.1; SessionId: 6F57A4A404D2BC3CD82391F6973FA715; Granted Authorities: ROLE_BRONZE_SUB, attributes : xxxxxx
12890 [http-bio-8080-exec-14] DEBUG org.springframework.security.access.vote.AffirmativeBased - Voter: org.springframework.security.web.access.expression.WebExpressionVoter#b041b0, returned: -1
12890 [http-bio-8080-exec-14] DEBUG org.springframework.security.web.access.ExceptionTranslationFilter - Access is denied (user is not anonymous); delegating to AccessDeniedHandler
org.springframework.security.access.AccessDeniedException: Access is denied
I have seen a number of posts on StackOverflow that have a number of different formats for the regular expression, ranging from what I have above to
<security:intercept-url pattern="\A^/unjust?action=search.$\Z"
I have been able to generate matches using the ant path matcher, however, things then fail when I have a request like this:
/unjust?action=browseUNDT&sEcho=1&iColumns=7&sColumns=&iDisplayStart=0&iDisplayLength=10&mDataProp_0=caseName&mDataProp_1=caseId&mDataProp_2=judgmentDate&mDataProp_3=judgmentType&mDataProp_4=judgmentNo&mDataProp_5=docId&mDataProp_6=displayCase&sSearch=&bRegex=false&sSearch_0=&bRegex_0=false&bSearchable_0=true&sSearch_1=&bRegex_1=false&bSearchable_1=true&sSearch_2=&bRegex_2=false&bSearchable_2=true&sSearch_3=&bRegex_3=false&bSearchable_3=true&sSearch_4=&bRegex_4=false&bSearchable_4=true&sSearch_5=&bRegex_5=false&bSearchable_5=true&sSearch_6=&bRegex_6=false&bSearchable_6=true&iSortCol_0=0&sSortDir_0=asc&iSortingCols=1&bSortable_0=true&bSortable_1=true&bSortable_2=true&bSortable_3=true&bSortable_4=true&bSortable_5=true&bSortable_6=true&_=1379771161138
And everything in between.
Can someone please steer me towards the correct setup and syntax for this pattern matching?
Spring doesnt support parameter matching in url by default. with regex matcher using request-matcher="regex" or path-type="regex" you can use regex to match urls.
Now in the pattern you provided =/unjust?action=browse "?" means a special symbol in regex One occurence or Zero. You should escape ? in your pattern by using "\\?"
I'm using Spring-Security 3.1.RELEASE within a Spring Roo environment. I've changed the login mechanism so that it works with JSON-Response. For that I've created two classes (scala) namely
class JsonSuccessHandler extends SimpleUrlAuthenticationSuccessHandler {
override def onAuthenticationSuccess(request: HttpServletRequest, response: HttpServletResponse, authentication: Authentication) = {
val responseWrapper = new HttpServletResponseWrapper(response);
val out = responseWrapper.getWriter
out.write("{success: true}")
out.close
}
}
#Repository class JsonEntryPoint extends AuthenticationEntryPoint {
def commence(request: HttpServletRequest, response: HttpServletResponse, ae: AuthenticationException) = {
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized")
}
}
The entries in application-security.xml are as follows:
<http use-expressions="true" entry-point-ref="jsonAuthenticationEntryPoint">
<custom-filter ref="myFilter" position="FORM_LOGIN_FILTER"/>
<custom-filter ref="rememberMeFilter" position="REMEMBER_ME_FILTER"/>
<logout logout-url="/resources/j_spring_security_logout"/>
<intercept-url pattern="/backend/**" access="isAuthenticated()" />
<intercept-url pattern="/resources/**" access="permitAll" />
<intercept-url pattern="/admin/**" access="hasRole('ROLE_ADMIN')" />
<intercept-url pattern="/**" access="permitAll" />
</http>
<beans:bean id="rememberMeFilter" class="org.springframework.security.web.authentication.rememberme.RememberMeAuthenticationFilter">
<beans:property name="rememberMeServices" ref="rememberMeServices"/>
<beans:property name="authenticationManager" ref="authenticationManager" />
</beans:bean>
<beans:bean id="rememberMeServices" class="org.springframework.security.web.authentication.rememberme.JdbcTokenBasedRememberMeServices">
<beans:property name="userDetailsService" ref="userServiceDb"/>
<beans:property name="tokenRepository" ref="tokenRepository"/>
<beans:property name="key" value="reservation" />
</beans:bean>
<beans:bean id="tokenRepository" class="org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl">
<beans:property name="dataSource" ref="dataSource"/>
</beans:bean>
<beans:bean id="myFilter" class="org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter">
<beans:property name="authenticationManager" ref="authenticationManager" />
<beans:property name="authenticationSuccessHandler" ref="mySuccessHandler" />
<beans:property name="rememberMeServices" ref="rememberMeServices" />
<beans:property name="usernameParameter" value="email" />
<beans:property name="passwordParameter" value="password" />
</beans:bean>
<beans:bean id="mySuccessHandler" class="JsonSuccessHandler"/>
<beans:bean id="jsonAuthenticationEntryPoint" class="JsonEntryPoint" />
Authentication is working fine, so I can successfully login and also concerning remember-me the data is stored successfully into the database (table persistent_logins). But when I restart the server the Session is as expected deleted and the remember me functionality should do its work. Unfortunately it fails with following log data:
[INFO] Started Jetty Server
17:19:15.867 [qtp1943091306-38] DEBUG o.s.security.web.FilterChainProxy - / at position 1 of 10 in additional filter chain; firing Filter: 'SecurityContextPersistenceFilter'
17:19:15.875 [qtp1943091306-38] DEBUG o.s.s.w.c.HttpSessionSecurityContextRepository - No HttpSession currently exists
17:19:15.875 [qtp1943091306-38] DEBUG o.s.s.w.c.HttpSessionSecurityContextRepository - No SecurityContext was available from the HttpSession: null. A new one will be created.
17:19:15.877 [qtp1943091306-38] DEBUG o.s.security.web.FilterChainProxy - / at position 2 of 10 in additional filter chain; firing Filter: 'LogoutFilter'
17:19:15.878 [qtp1943091306-38] DEBUG o.s.security.web.FilterChainProxy - / at position 3 of 10 in additional filter chain; firing Filter: 'UsernamePasswordAuthenticationFilter'
17:19:15.878 [qtp1943091306-38] DEBUG o.s.security.web.FilterChainProxy - / at position 4 of 10 in additional filter chain; firing Filter: 'RequestCacheAwareFilter'
17:19:15.878 [qtp1943091306-38] DEBUG o.s.security.web.FilterChainProxy - / at position 5 of 10 in additional filter chain; firing Filter: 'SecurityContextHolderAwareRequestFilter'
17:19:15.879 [qtp1943091306-38] DEBUG o.s.security.web.FilterChainProxy - / at position 6 of 10 in additional filter chain; firing Filter: 'RememberMeAuthenticationFilter'
17:19:15.879 [qtp1943091306-38] DEBUG o.s.s.w.a.r.PersistentTokenBasedRememberMeServices - Remember-me cookie detected
17:19:15.895 [qtp1943091306-38] DEBUG o.s.jdbc.core.JdbcTemplate - Executing prepared SQL query
17:19:15.895 [qtp1943091306-38] DEBUG o.s.jdbc.core.JdbcTemplate - Executing prepared SQL statement [select username,series,token,last_used from persistent_logins where series = ?]
17:19:15.896 [qtp1943091306-38] DEBUG o.s.jdbc.datasource.DataSourceUtils - Fetching JDBC Connection from DataSource
17:19:15.922 [qtp1943091306-38] DEBUG o.s.jdbc.datasource.DataSourceUtils - Returning JDBC Connection to DataSource
17:19:15.938 [qtp1943091306-38] DEBUG o.s.s.w.a.r.PersistentTokenBasedRememberMeServices - Refreshing persistent login token for user 'physio1#physio-termin.at', series 'oLmZMQbnFsfyTziANriMKw=='
17:19:15.939 [qtp1943091306-38] DEBUG o.s.jdbc.core.JdbcTemplate - Executing prepared SQL update
17:19:15.944 [qtp1943091306-38] DEBUG o.s.jdbc.core.JdbcTemplate - Executing prepared SQL statement [update persistent_logins set token = ?, last_used = ? where series = ?]
17:19:15.944 [qtp1943091306-38] DEBUG o.s.jdbc.datasource.DataSourceUtils - Fetching JDBC Connection from DataSource
17:19:15.984 [qtp1943091306-38] DEBUG o.s.jdbc.core.JdbcTemplate - SQL update affected 1 rows
17:19:15.992 [qtp1943091306-38] DEBUG o.s.jdbc.datasource.DataSourceUtils - Returning JDBC Connection to DataSource
17:19:15.996 [qtp1943091306-38] DEBUG o.s.jdbc.core.JdbcTemplate - Executing prepared SQL query
17:19:15.996 [qtp1943091306-38] DEBUG o.s.jdbc.core.JdbcTemplate - Executing prepared SQL statement [select email username, password, isactive enabled from principal where email = ?]
17:19:15.996 [qtp1943091306-38] DEBUG o.s.jdbc.datasource.DataSourceUtils - Fetching JDBC Connection from DataSource
17:19:16.001 [qtp1943091306-38] DEBUG o.s.jdbc.datasource.DataSourceUtils - Returning JDBC Connection to DataSource
17:19:16.013 [qtp1943091306-38] DEBUG o.s.jdbc.core.JdbcTemplate - Executing prepared SQL query
17:19:16.013 [qtp1943091306-38] DEBUG o.s.jdbc.core.JdbcTemplate - Executing prepared SQL statement [select p.email username, a.authority from principal p inner join principal_authority apa on p.id = apa.principal_id inner join authority a on a.id = apa.authorities_id where p.email = ?]
17:19:16.013 [qtp1943091306-38] DEBUG o.s.jdbc.datasource.DataSourceUtils - Fetching JDBC Connection from DataSource
17:19:16.015 [qtp1943091306-38] DEBUG o.s.jdbc.datasource.DataSourceUtils - Returning JDBC Connection to DataSource
17:19:16.021 [qtp1943091306-38] DEBUG o.s.s.w.a.r.PersistentTokenBasedRememberMeServices - Remember-me cookie accepted
17:19:16.036 [qtp1943091306-38] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Returning cached instance of singleton bean 'org.springframework.context.annotation.internalScheduledAnnotationProcessor'
17:19:16.042 [qtp1943091306-38] DEBUG o.s.s.w.a.r.RememberMeAuthenticationFilter - SecurityContextHolder not populated with remember-me token, as AuthenticationManager rejected Authentication returned by RememberMeServices: 'org.springframework.security.authentication.RememberMeAuthenticationToken#2fb6ff6b: Principal: org.springframework.security.core.userdetails.User#d45302c2: Username: physio1#physio-termin.at; Password: [PROTECTED]; Enabled: true; AccountNonExpired: true; credentialsNonExpired: true; AccountNonLocked: true; Granted Authorities: ROLE_PHYSIOTHERAPEUT; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails#957e: RemoteIpAddress: 127.0.0.1; SessionId: null; Granted Authorities: ROLE_PHYSIOTHERAPEUT'; invalidating remember-me token
org.springframework.security.authentication.ProviderNotFoundException: No AuthenticationProvider found for org.springframework.security.authentication.RememberMeAuthenticationToken
at org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:196) ~[spring-security-core-3.1.0.RELEASE.jar:3.1.0.RELEASE]
at org.springframework.security.web.authentication.rememberme.RememberMeAuthenticationFilter.doFilter(RememberMeAuthenticationFilter.java:102) ~[spring-security-web-3.1.0.RELEASE.jar:3.1.0.RELEASE]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:323) [spring-security-web-3.1.0.RELEASE.jar:3.1.0.RELEASE]
What am I missing? I dont't understand why the ProviderNotFoundException is thrown.
You're almost done, authentication works OK, you forgot to add RememberMeAuthenticationProvider. If you use security namespace to create AuthenticationManager, just add separate <authentication-provider> like here:
<authentication-manager>
<authentication-provider ref="yourRegularAuthenticationProvider" />
<authentication-provider ref="rememberMeAuthenticationProvider" />
</authentication-manager>
<beans:bean id="rememberMeAuthenticationProvider" class="org.springframework.security.authentication.RememberMeAuthenticationProvider">
<beans:property name="key" value="reservation" />
</beans:bean>
my application compiles to ROOT.war which basically means I do not have a context root other than /. There are pages which are to be secured; however there are URLs which do not need it; for example my http://localhost:8080/ gives the home page of this application; and there are similar pages like about us, contact us, etc where security is not need. So I add this to the configuration
<security:intercept-url pattern="/" access="permitAll" />
<security:intercept-url pattern="/resources/**" access="permitAll" />
<security:intercept-url pattern="/register/confirm" access="isAuthenticated()" />
<security:intercept-url pattern="/register/accept" access="isAuthenticated()" />
<security:intercept-url pattern="/shopper/**" access="isAuthenticated()" />
but this just says that users are allowed to access these URL "without authentication"if you access these URLs the security filters are all applied, as seen below in the debug logs
DEBUG: org.springframework.security.web.FilterChainProxy - / at position 1 of 12 in additional filter chain; firing Filter: 'ConcurrentSessionFilter'
DEBUG: org.springframework.security.web.FilterChainProxy - / at position 2 of 12 in additional filter chain; firing Filter: 'SecurityContextPersistenceFilter'
DEBUG: org.springframework.security.web.context.HttpSessionSecurityContextRepository - No HttpSession currently exists
DEBUG: org.springframework.security.web.context.HttpSessionSecurityContextRepository - No SecurityContext was available from the HttpSession: null. A new one will be created.
DEBUG: org.springframework.security.web.FilterChainProxy - / at position 3 of 12 in additional filter chain; firing Filter: 'LogoutFilter'
DEBUG: org.springframework.security.web.FilterChainProxy - / at position 4 of 12 in additional filter chain; firing Filter: 'UsernamePasswordAuthenticationFilter'
DEBUG: org.springframework.security.web.FilterChainProxy - / at position 5 of 12 in additional filter chain; firing Filter: 'OpenIDAuthenticationFilter'
DEBUG: org.springframework.security.web.FilterChainProxy - / at position 6 of 12 in additional filter chain; firing Filter: 'BasicAuthenticationFilter'
DEBUG: org.springframework.security.web.FilterChainProxy - / at position 7 of 12 in additional filter chain; firing Filter: 'RequestCacheAwareFilter'
DEBUG: org.springframework.security.web.FilterChainProxy - / at position 8 of 12 in additional filter chain; firing Filter: 'SecurityContextHolderAwareRequestFilter'
DEBUG: org.springframework.security.web.FilterChainProxy - / at position 9 of 12 in additional filter chain; firing Filter: 'AnonymousAuthenticationFilter'
DEBUG: org.springframework.security.web.authentication.AnonymousAuthenticationFilter - Populated SecurityContextHolder with anonymous token: 'org.springframework.security.authentication.AnonymousAuthenticationToken#9055e4a6: Principal: anonymousUser; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails#957e: RemoteIpAddress: 127.0.0.1; SessionId: null; Granted Authorities: ROLE_ANONYMOUS'
DEBUG: org.springframework.security.web.FilterChainProxy - / at position 10 of 12 in additional filter chain; firing Filter: 'SessionManagementFilter'
DEBUG: org.springframework.security.web.FilterChainProxy - / at position 11 of 12 in additional filter chain; firing Filter: 'ExceptionTranslationFilter'
DEBUG: org.springframework.security.web.FilterChainProxy - / at position 12 of 12 in additional filter chain; firing Filter: 'FilterSecurityInterceptor'
DEBUG: org.springframework.security.web.util.AntPathRequestMatcher - Checking match of request : '/'; against '/'
DEBUG: org.springframework.security.web.access.intercept.FilterSecurityInterceptor - Secure object: FilterInvocation: URL: /; Attributes: [permitAll]
DEBUG: org.springframework.security.web.access.intercept.FilterSecurityInterceptor - Previously Authenticated: org.springframework.security.authentication.AnonymousAuthenticationToken#9055e4a6: Principal: anonymousUser; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails#957e: RemoteIpAddress: 127.0.0.1; SessionId: null; Granted Authorities: ROLE_ANONYMOUS
DEBUG: org.springframework.security.access.vote.AffirmativeBased - Voter: org.springframework.security.web.access.expression.WebExpressionVoter#2b06c17b, returned: 1
DEBUG: org.springframework.security.web.access.intercept.FilterSecurityInterceptor - Authorization successful
DEBUG: org.springframework.security.web.access.intercept.FilterSecurityInterceptor - RunAsManager did not change Authentication object
DEBUG: org.springframework.security.web.FilterChainProxy - / reached end of additional filter chain; proceeding with original chain
DEBUG: org.springframework.web.servlet.DispatcherServlet - DispatcherServlet with name 'appServlet' processing GET request for [/
]
When I try using this configuration (in sequence mentioned below):
<!-- No Security required for the ROOT Context -->
<security:http pattern="/**" security="none" />
<!-- Apply secyrity for shopper URLs -->
<security:http auto-config="true" use-expressions="true" access-denied-page="/denied">
<security:intercept-url pattern="/" access="permitAll" />
<security:intercept-url pattern="/resources/**" access="permitAll" />
<security:intercept-url pattern="/register/confirm" access="isAuthenticated()" />
<security:intercept-url pattern="/register/accept" access="isAuthenticated()" />
<security:intercept-url pattern="/shopper/**" access="isAuthenticated()" /
....
</security:http>
<security:http pattern="/resources/**" security="none" />
It breaks down giving the error
DEBUG: org.springframework.security.web.util.AntPathRequestMatcher - Checking match of request : '/auth/login'; against '/'
DEBUG: org.springframework.security.web.util.AntPathRequestMatcher - Checking match of request : '/auth/login'; against '/resources/**'
DEBUG: org.springframework.security.web.util.AntPathRequestMatcher - Checking match of request : '/auth/login'; against '/register/confirm'
DEBUG: org.springframework.security.web.util.AntPathRequestMatcher - Checking match of request : '/auth/login'; against '/register/accept'
DEBUG: org.springframework.security.web.util.AntPathRequestMatcher - Checking match of request : '/auth/login'; against '/shopper/**'
DEBUG: org.springframework.security.config.http.DefaultFilterChainValidator - No access attributes defined for login page URL
INFO : org.springframework.beans.factory.support.DefaultListableBeanFactory - Destroying singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory#5bebacc8: defining beans [placeholderConfig,dataSource,entityManagerFactory,org.springframework.aop.config.internalAutoProxyCreator,org.springframework.transaction.annotation.AnnotationTransactionAttributeSource#0,org.springframework.transaction.interceptor.TransactionInterceptor#0,org.springframework.transaction.config.internalTransactionAdvisor,transactionManager,org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor#0,registrationService,shopperService,org.springframework.context.annotation.internalConfigurationAnnotationProcessor,org.springframework.context.annotation.internalAutowiredAnnotationProcessor,org.springframework.context.annotation.internalRequiredAnnotationProcessor,org.springframework.context.annotation.internalCommonAnnotationProcessor,org.springframework.context.annotation.internalPersistenceAnnotationProcessor,org.springframework.security.filterChains,org.springframework.security.filterChainProxy,org.springframework.security.web.PortMapperImpl#0,org.springframework.security.config.authentication.AuthenticationManagerFactoryBean#0,org.springframework.security.authentication.ProviderManager#0,org.springframework.security.web.context.HttpSessionSecurityContextRepository#0,org.springframework.security.core.session.SessionRegistryImpl#0,org.springframework.security.web.authentication.session.ConcurrentSessionControlStrategy#0,org.springframework.security.web.savedrequest.HttpSessionRequestCache#0,org.springframework.security.web.access.expression.DefaultWebSecurityExpressionHandler#0,org.springframework.security.access.vote.AffirmativeBased#0,org.springframework.security.web.access.intercept.FilterSecurityInterceptor#0,org.springframework.security.web.access.DefaultWebInvocationPrivilegeEvaluator#0,org.springframework.security.authentication.AnonymousAuthenticationProvider#0,org.springframework.security.web.authentication.www.BasicAuthenticationEntryPoint#0,org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter#0,org.springframework.security.openid.OpenIDAuthenticationFilter#0,org.springframework.security.openid.OpenIDAuthenticationProvider#0,org.springframework.security.userDetailsServiceFactory,org.springframework.security.web.DefaultSecurityFilterChain#0,org.springframework.security.web.DefaultSecurityFilterChain#1,org.springframework.security.authentication.dao.DaoAuthenticationProvider#0,org.springframework.security.authentication.DefaultAuthenticationEventPublisher#0,org.springframework.security.authenticationManager,passwordEncoder,registrationAwareUserDetailsService,registrationAwareAuthSuccessHandler,org.springframework.context.annotation.ConfigurationClassPostProcessor$ImportAwareBeanPostProcessor#0]; root of factory hierarchy
INFO : org.hibernate.impl.SessionFactoryImpl - closing
ERROR: org.springframework.web.context.ContextLoader - Context initialization failed
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.security.filterChainProxy': Invocation of init method failed; nested exception is java.lang.IllegalArgumentException: A universal match pattern ('/**') is defined before other patterns in the filter chain, causing them to be ignored. Please check the ordering in your <security:http> namespace or FilterChainProxy bean configuration
I cannot really understand the reason behind this. Do I have to implement my own request pattern matcher?
Solution
<beans:bean id="filterChainProxy" class="org.springframework.security.web.FilterChainProxy" >
<beans:constructor-arg>
<beans:list>
<security:filter-chain pattern="/resources/**"
filters="none" />
<security:filter-chain pattern="/aboutus"
filters="none" />
<security:filter-chain pattern="/contactus"
filters="none" />
<security:filter-chain pattern="/news"
filters="none" />
</beans:list>
</beans:constructor-arg>
</beans:bean>
security="none" with pattern /** captures all URLs, so no other rule could be applied. What's why you receive error in second example.
But it's possible to define different filter-chains for different URL-patterns. I don't have example of this with new syntax, but here is example with old syntax (order of filter-chains is important):
<bean id="springSecurityFilterChain" class="org.springframework.security.web.FilterChainProxy">
<sec:filter-chain-map path-type="ant">
<sec:filter-chain pattern="/dwr/**" filters="securityContextPersistenceFilter,securityContextHolderAwareRequestFilter,rememberMeAuthenticationFilter,anonymousAuthenticationFilter" />
<sec:filter-chain pattern="/**" filters="channelProcessingFilter,securityContextPersistenceFilter,logoutFilter,authenticationFilter,securityContextHolderAwareRequestFilter,rememberMeAuthenticationFilter,anonymousAuthenticationFilter,sessionManagementFilter,exceptionTranslationFilter,filterSecurityInterceptor,switchUserProcessingFilter" />
</sec:filter-chain-map>
</bean>
Here's the syntax I have finally chosen to stick with; for it makes the XML a lot easier to read and comprehend
<!-- Non secure URLs -->
<security:http pattern="/" security='none' />
<security:http pattern="/home" security='none' />
<security:http pattern="/aboutus" security='none' />
...
...
<security:http pattern="/resources/**" security='none' />
<security:http pattern="/favicon.ico" security='none' />
<!-- URLs under security config -->
<security:http auto-config="true" use-expressions="true" pattern="/admin/**" access-denied-page="/denied">
<security:intercept-url pattern="/admin/**" access="ROLE_ADMIN" requires-channel="https" />
</security:http>
All I want, is a simple remember-me. I read http://static.springsource.org/spring-security/site/docs/3.0.x/reference/remember-me.html
What I have done so far:
Created my own UserDetailsService to work with Hibernate / JPA. My impl. does not consider any remember-me stuff
Considered configuration through appContext <security:remember-me key="89dqj219dn910lsAc12" user-service-ref="jpaUserDetailsService" token-validity-seconds="864000"/>
Checked, that the cookie SPRING_SECURITY_REMEMBER_ME_COOKIE is really set
Logged in to the secured site and it works
When I restart my browser, I keep getting an error:
org.springframework.security.access.AccessDeniedException: Access is denied
Authentication object as a String: org.springframework.security.authentication.RememberMeAuthenticationToken#9ab72a70: Principal: de.myapp.businessobjects.AppUser#61f68b18: Username: myad; Password: [PROTECTED]; Enabled: true; AccountNonExpired: true; credentialsNonExpired: true; AccountNonLocked: true; PersonalInformation: 65537; ; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails#957e: RemoteIpAddress: 127.0.0.1; SessionId: null; Granted Authorities: ROLE_ADMIN, ROLE_USER
And here is my secContext.xml:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:security="http://www.springframework.org/schema/security"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security-3.0.xsd">
<security:global-method-security pre-post-annotations="enabled">
</security:global-method-security>
<security:http use-expressions="true" access-denied-page="/accessDenied">
<security:form-login
login-page="/login"
login-processing-url="/loginProcess"
default-target-url="/intro"
authentication-failure-url="/login?login_error=1"
/>
<security:logout
logout-url="/logout"
logout-success-url="/logoutSuccess"/>
<security:intercept-url pattern="/**" access="permitAll"/>
<security:intercept-url pattern="/login" access="permitAll"/>
<security:intercept-url pattern="/styles/**" access="permitAll"/>
<security:intercept-url pattern="/scripts/**" access="permitAll"/>
<security:remember-me key="89dqj219dn910lsAc12" user-service-ref="jpaUserDetailsService"
token-validity-seconds="864000"/>
</security:http>
<security:authentication-manager alias="authenticationManager">
<security:authentication-provider user-service-ref="jpaUserDetailsService">
<security:password-encoder hash="sha">
</security:password-encoder>
</security:authentication-provider>
</security:authentication-manager>
<bean id="rememberMeFilter" class=
"org.springframework.security.web.authentication.rememberme.RememberMeAuthenticationFilter">
<property name="rememberMeServices" ref="rememberMeServices"/>
<property name="authenticationManager" ref="authenticationManager"/>
</bean>
<bean id="rememberMeServices" class=
"org.springframework.security.web.authentication.rememberme.TokenBasedRememberMeServices">
<property name="userDetailsService" ref="jpaUserDetailsService"/>
<property name="key" value="89dqj219dn910lsAc12"/>
</bean>
<bean id="rememberMeAuthenticationProvider" class=
"org.springframework.security.authentication.RememberMeAuthenticationProvider">
<property name="key" value="89dqj219dn910lsAc12"/>
</bean>
</beans>
and finally some debug trace
03:45:14.598 [7225609#qtp-10131947-7] DEBUG o.s.w.b.a.s.HandlerMethodInvoker - Invoking request handler method: public java.lang.String de.myapp.controller.bstController.showbstpage(java.lang.String,javax.servlet.http.HttpServletResponse)
03:45:14.598 [7225609#qtp-10131947-7] DEBUG o.s.s.a.i.a.MethodSecurityInterceptor - Secure object: ReflectiveMethodInvocation: public java.lang.String de.myapp.controller.bstController.showbstpage(java.lang.String,javax.servlet.http.HttpServletResponse); target is of class [de.myapp.controller.bstController]; Attributes: [[authorize: 'isFullyAuthenticated() and #username == principal.username', filter: 'null', filterTarget: 'null']]
03:45:14.598 [7225609#qtp-10131947-7] DEBUG o.s.s.a.i.a.MethodSecurityInterceptor - Previously Authenticated: org.springframework.security.authentication.RememberMeAuthenticationToken#9ab72a70: Principal: de.myapp.businessobjects.AppUser#61f68b18: Username: myad; Password: [PROTECTED]; Enabled: true; AccountNonExpired: true; credentialsNonExpired: true; AccountNonLocked: true; PersonalInformation: 65537; ; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails#957e: RemoteIpAddress: 127.0.0.1; SessionId: null; Granted Authorities: ROLE_ADMIN, ROLE_USER
03:45:14.599 [7225609#qtp-10131947-7] DEBUG o.s.c.c.s.GenericConversionService - Converting value false of [TypeDescriptor java.lang.Boolean] to [TypeDescriptor java.lang.Boolean]
03:45:14.599 [7225609#qtp-10131947-7] TRACE o.s.c.c.s.GenericConversionService - Matched cached converter NO_OP
03:45:14.599 [7225609#qtp-10131947-7] DEBUG o.s.c.c.s.GenericConversionService - Converted to false
03:45:14.599 [7225609#qtp-10131947-7] DEBUG o.s.s.access.vote.AffirmativeBased - Voter: org.springframework.security.access.prepost.PreInvocationAuthorizationAdviceVoter#a866a9, returned: -1
03:45:14.599 [7225609#qtp-10131947-7] DEBUG o.s.s.access.vote.AffirmativeBased - Voter: org.springframework.security.access.vote.RoleVoter#1ebf305, returned: 0
03:45:14.599 [7225609#qtp-10131947-7] DEBUG o.s.s.access.vote.AffirmativeBased - Voter: org.springframework.security.access.vote.AuthenticatedVoter#19ffd6f, returned: 0
I really don't know where to continue debugging. What have I missed? Do I have to create my own implementation of remember-me?
I would really appreciate a working sample application that demonstrates the default implementation of springs remember-me...
-------- EDIT -----------
I have just compiled and run the remember-me reference app by springsecurity itself: the spring-security\samples\tutorial account app AND the contact app. Actually, I have exactly the same problem?!?. I have tried firefox, opera and ie ... I am shattered ...
It looks like remember-me authentication was working fine in your application, since you obtain a valid authentication token from the remember-me cookie.
However, the log output indicates that there is a method access control annotation on a controller method bstController.showbstpage which requires "full" authentication, from the expression isFullyAuthenticated() and #username == principal.username. Remember-me doesn't qualify as full authentication, hence the expression rejects the current authentication.
As an aside the intercept-url elements are wrongly ordered since /** is at the top and will be applied to all requests, making the others redundant.
Also, it wouldn't be possible to get the same issue with the sample applications, since they don't require full authentication for any operation, so you must have had some other issue there.
When you login, is the "password" field on your UserDetails object being set to non-null / non-empty value? In my application, the actual authentication is delegated off to another system and I don't store the user-submitted password on my UserDetails object. I couldn't get the RememberMe cookie to work until I set the password property to a value. In my case, I simply defaulted the property to the word "password" just so it wouldn't be null/empty string.
I don't know if that's anything like your scenario, but this drove me crazy until I figured it out.