I have a little problem with spring security :)
What is my goal: Configure LDAP auth with custom roles, fetched from database, and remember me functionality.
What is done:
My problem is: 'Remember me' works fine, persistent_logins table created successfully, it stores tokens fine. But when user returns to web-site, spring shows 'not authorized' page.
I think it happens because 'Remember me' knows nothing about my custom roles, and fetching roles from LDAP.
Question is: How to tell to 'Remember me' to get roles via my CustomLdapAuthoritiesPopulator?
my applicationContext.xml
<bean id="contextSource" class="org.springframework.security.ldap.DefaultSpringSecurityContextSource">
<constructor-arg value="ldap://ldap.forumsys.com:389"/>
<property name="userDn" value="cn=read-only-admin,dc=example,dc=com"/>
<property name="password" value="password"/>
</bean>
<bean name="myDataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/db"/>
<property name="username" value="username"/>
<property name="password" value="password"/>
</bean>
<bean id="ldapAuthProvider" class="org.springframework.security.ldap.authentication.LdapAuthenticationProvider">
<constructor-arg>
<bean class="org.springframework.security.ldap.authentication.BindAuthenticator">
<constructor-arg ref="contextSource"/>
<property name="userDnPatterns">
<list>
<value>uid={0},dc=example,dc=com</value>
</list>
</property>
</bean>
</constructor-arg>
<constructor-arg>
<bean class="my.extra.CustomLdapAuthoritiesPopulator"/>
</constructor-arg>
</bean>
<bean id="tokenRepository"
class="org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl">
<property name="createTableOnStartup" value="false"/>
<property name="dataSource" ref="myDataSource"/>
</bean>
<security:authentication-manager>
<security:authentication-provider ref="ldapAuthProvider"/>
</security:authentication-manager>
<security:http auto-config="true" use-expressions="true">
<security:access-denied-handler error-page="/403"/>
<security:intercept-url pattern="/login*" access="permitAll()"/>
<security:intercept-url pattern="/favicon.ico" access="permitAll()"/>
<security:intercept-url pattern="/resources/**" access="permitAll()"/>
<security:intercept-url pattern="/**" access="hasRole('ROLE_USER')"/>
<security:form-login login-page='/login' login-processing-url="/j_spring_security_check"
default-target-url="/" authentication-failure-url="/login?fail"/>
<security:remember-me key="_spring_security_remember_me" token-validity-seconds="14400"
token-repository-ref="tokenRepository"
user-service-ref="ldapUserService"/>
</security:http>
<security:ldap-user-service id="ldapUserService" server-ref="contextSource"
group-search-base="dc=example,dc=com"
group-search-filter="ou={0})"
user-search-base="dc=example,dc=com"
user-search-filter="uid={0}"/>
During debugging, when user returns, CustomLdapAuthoritiesPopulator not called. I added code to check roles for user (on welcome page, and on custom 403 page).
Collection<? extends GrantedAuthority> roles = SecurityContextHolder.getContext().getAuthentication().getAuthorities();
roles.forEach(System.out::println);
After user logs in, welcome page shows "ROLE_USER", "ROLE_ADMIN"
After user returns back, 403 page shows ""; (nothing)
From the Spring Docs Remember-Me
If you are using an authentication provider which doesn't use a
UserDetailsService(for example, the LDAP provider) then it won't work unless you also have a UserDetailsService bean in your application context.
Try adding below in your applicationContext.xml:-
Add RoleVoter. From Spring Docs
Votes if any
ConfigAttribute.getAttribute()starts with a prefix indicating that it is a role. The default prefix string isROLE_, but this may be overridden to any value. It may also be set to empty, which means that essentially any attribute will be voted on. As described further below, the effect of an empty prefix may not be quite desirable.Abstains from voting if no configuration attribute commences with the role prefix. Votes to grant access if there is an exact matching
GrantedAuthorityto aConfigAttributestarting with the role prefix. Votes to deny access if there is no exact matching GrantedAuthority to a ConfigAttribute starting with the role prefix.All comparisons and prefixes are case sensitive.
<bean id="roleVoter"
class="org.springframework.security.access.vote.RoleVoter" p:rolePrefix="" />
Add AuthenticatedVoter. From Soring Docs
Votes if a
ConfigAttribute.getAttribute()ofIS_AUTHENTICATED_FULLYorIS_AUTHENTICATED_REMEMBEREDorIS_AUTHENTICATED_ANONYMOUSLYis present. This list is in order of most strict checking to least strict checking.The current Authentication will be inspected to determine if the principal has a particular level of authentication. The "FULLY" authenticated option means the user is authenticated fully (i.e.
AuthenticationTrustResolver.isAnonymous(Authentication)is false andAuthenticationTrustResolver.isRememberMe(Authentication)is false). The"REMEMBERED"will grant access if the principal was either authenticated via remember-me OR is fully authenticated. The"ANONYMOUSLY"will grant access if the principal was authenticated via remember-me, OR anonymously, OR via full authentication.All comparisons and prefixes are case sensitive.
<bean id="authVoter"
class="org.springframework.security.access.vote.AuthenticatedVoter">
</bean>
Now, configure a Spring AccessDecisionManager which utilizes the two voters above, in order to determine if a user was granted the correct authority to access a resource.
<bean id="accessDecisionManager" class="org.springframework.security.access.vote.ConsensusBased">
<property name="allowIfAllAbstainDecisions" value="false" />
<property name="decisionVoters">
<list>
<ref bean="roleVoter" />
<ref bean="authVoter" />
</list>
</property>
</bean>
For more details: Visit Spring security 3 remember-me with LDAP authentication
EDIT 1:
EDIT 2:
Below source is originally posted at Configuring Spring Security Form Login with Remember-Me Enabled.
Create Custom RememberMeProcessingFilter:
public class MyRememberMeProcessingFilter extends RememberMeProcessingFilter {
private myService;
@Override
protected void onSuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, Authentication authResult) {
// perform some custom logic when the user has been 'remembered' & authenticated - e.g. update a login count etc
this.myService.doSomeCustomBusinessLogic(authResult.getName());
super.onSuccessfulAuthentication(request, response, authResult);
}
}
RememberMeProcessingFilterto include any custom business logic you wish to run when a user returns to your site and is ‘remembered’ by the application.
Don’t forget to add:
<custom-authentication-provider />
This makes sure that remember me is actually used as an authentication provider - i.e. when your user returns having previously asked to be remembered, this adds remember me to the list of providers that check if the user is authenticated.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With