Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring Security with LDAP and Database roles

In our new insurance project, I am trying to implement spring-security with Ldap active-directory.

I want to just check username/password against AD, once user found in AD. I want to authorize him from user table(app authorized users) with access levels in database. Could someone give sample/point me for a good resource.

like image 734
Jon Smith Avatar asked May 22 '13 17:05

Jon Smith


2 Answers

The easiest way to achieve this now (Spring Security 3.2.5.RELEASE) is by implementing a custom LdapAuthoritiesPopulator which uses a custom JdbcDaoImpl to obtain the authorities from the database.

Code

Assuming you are using the default database schema, and that you are using the same username for authentication in LDAP and as the foreign key in the authorities table, you only need this:

package demo;

import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Collection;
import java.util.List;

import org.springframework.jdbc.core.RowMapper;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.core.userdetails.jdbc.JdbcDaoImpl;

import org.springframework.ldap.core.DirContextOperations;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.ldap.userdetails.LdapAuthoritiesPopulator;

/*
 * You need to extend JdbcDaoImpl to expose the protected method loadUserAuthorities.
 */
public class CustomJdbcUserDetailsService extends JdbcDaoImpl {

    @Override
    public List<GrantedAuthority> loadUserAuthorities(String username) {
        return super.loadUserAuthorities(username);
    }
}


/*
 * Then, the only thing your populator needs to do is use the custom UserDetailsService above.
 */
public class CustomLdapAuthoritiesPopulator implements LdapAuthoritiesPopulator {

    private static final Logger LOGGER = LoggerFactory.getLogger(CustomLdapAuthoritiesPopulator.class);

    private CustomJdbcUserDetailsService service;

    public CustomLdapAuthoritiesPopulator(CustomJdbcUserDetailsService service) {
        this.service = service;
    }

    public Collection<? extends GrantedAuthority> getGrantedAuthorities(DirContextOperations user, String username) {
        return service.loadUserAuthorities(username);
    }

}

The only thing left now is configure the LDAP authentication provider to use CustomLdapAuthoritiesPopulator.

Java Config

In a @Configuration annotated subclass of GlobalMethodSecurityConfiguration or WebSecurityConfigurerAdapter (depending on your case), add the following:

@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {

    /* other authentication configurations you might have */

    /*
     * This assumes that the dataSource configuring
     * the connection to the database has been Autowired
     * into this bean.
     *
     * Adapt according to your specific case.
     */
    CustomJdbcUserDetailsService customJdbcUserDetailsService = new CustomJdbcUserDetailsService();
    customJdbcUserDetailsService.setDataSource(dataSource);

    CustomLdapAuthoritiesPopulator customLdapAuthoritiesPopulator = new CustomLdapAuthoritiesPopulator(customJdbcUserDetailsService);

    auth.ldapAuthentication().ldapAuthoritiesPopulator(customLdapAuthoritiesPopulator)/* other LDAP configurations you might have */;

    /* yet more authentication configurations you might have */
}

Refer to https://github.com/pfac/howto-spring-security for a working example.

XML Config

Disclaimer: I've been working solely with Java configuration, so tread cautiously, there might be some errors.

Unlike other configurations for authenticating with LDAP, there seems to be no pretty XML tags to customize the LdapAuthoritiesPopulator. So, it has to be done manually. Assuming a bean contextSource configuring the connection to the LDAP server has been defined, add the following to your Spring XML configuration:

<beans:bean id="customJdbcUserDetailsService" class="demo.CustomJdbcUserDetailsService" />
<beans:bean id="customLdapAuthoritiesPopulator" class="demo.CustomLdapAuthoritiesPopulator">
    <beans:constructor-arg ref="customJdbcUserDetailsService" />
</beans:bean>

<beans:bean id="ldapAuthProvider" class="org.springframework.security.ldap.authentication.LdapAuthenticationProvider">
    <beans:constructor-arg>
        <beans:bean class="org.springframework.security.ldap.authentication.BindAuthenticator">
            <beans:constructor-arg ref="contextSource" />
            <!--
                other configurations you might need
            -->
        </beans:bean>
    </beans:constructor-arg>
    <beans:constructor-arg ref="customLdapAuthoritiesPopulator" />
</beans:bean>

<security:authentication-manager>
  <security:authentication-provider ref="ldapAuthProvider" />
</security:authentication-manager>

Source: http://spapas.github.io/2013/10/14/spring-ldap-custom-authorities/#spring-security-ldap-with-custom-authorities

like image 133
pfac Avatar answered Oct 18 '22 12:10

pfac


You will most likely have to do a custom UserDetailsServer, since your authenticating through LDAP but getting the roles through DB query. UserDetailsService is an interface. You would implement the interface then add your custom implementation to your Spring Security config doing something like:

<beans:bean id="userDetailsService" class="com.app.MyUserDetailsServiceImpl" />

<authentication-manager>
  <authentication-provider user-service-ref="userDetailsService">
    <password-encoder hash="plaintext" />
  </authentication-provider>
</authentication-manager>

In the loadUserByUsername(), you would create a UserDetails, setting the username, password, and the "authorities", which is the roles.

This Blog Post has an example on how to do it using a database, which you should be able to adapt to your requirements.

like image 45
CodeChimp Avatar answered Oct 18 '22 12:10

CodeChimp