Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring Security + LDAP + CustomLdapAuthoritiesPopulator + RememberMe

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:

  • LDAP Auth: OK
  • Custom roles for AD users from database: OK
  • Remember me: FAIL

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)

like image 923
EatingPeopleIsFun Avatar asked Dec 12 '25 22:12

EatingPeopleIsFun


1 Answers

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 is ROLE_, 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 GrantedAuthority to a ConfigAttribute starting 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() of IS_AUTHENTICATED_FULLY or IS_AUTHENTICATED_REMEMBERED or IS_AUTHENTICATED_ANONYMOUSLY is 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 and AuthenticationTrustResolver.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:

  1. Spring Security with LDAP and Database roles
  2. Spring Security 3.1 - Implement UserDetailsService with Spring Data JPA

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); 
    } 
} 

RememberMeProcessingFilter to 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.

like image 131
OO7 Avatar answered Dec 15 '25 10:12

OO7



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!