Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring security switch to Ldap authentication and database authorities

Tags:

I implemented database authentication for my web page and web service. It work well for both, now I have to add Ldap authentication. I have to authenticate through remote Ldap server (using username and password) and if the user exists I have to use my database for user roles (in my database username is the same username of Ldap). So I have to switch from my actual code to the Ldap and database authentication as above explained. My code is: SecurityConfig class

@Configuration @EnableWebSecurity @EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true, proxyTargetClass = true) public class SecurityConfig extends WebSecurityConfigurerAdapter {      @Autowired     @Qualifier("userDetailsService")     UserDetailsService userDetailsService;      @Autowired     public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {         auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());     }      @Bean     public PasswordEncoder passwordEncoder(){         PasswordEncoder encoder = new BCryptPasswordEncoder();         return encoder;     }      @Configuration     @Order(1)     public static class ApiWebSecurityConfig extends WebSecurityConfigurerAdapter{         @Override         protected void configure(HttpSecurity http) throws Exception {              http.csrf().disable()              .antMatcher("/client/**")              .authorizeRequests()              .anyRequest().authenticated()              .and()              .httpBasic();         }     }      @Configuration     @Order(2)     public static class FormWebSecurityConfig extends WebSecurityConfigurerAdapter{          @Override         public void configure(WebSecurity web) throws Exception {             web             //Spring Security ignores request to static resources such as CSS or JS files.             .ignoring()             .antMatchers("/static/**");         }          @Override         protected void configure(HttpSecurity http) throws Exception {             http             .authorizeRequests() //Authorize Request Configuration                 //the / and /register path are accepted without login                 //.antMatchers("/", "/register").permitAll()                 //the /acquisition/** need admin role                 //.antMatchers("/acquisition/**").hasRole("ADMIN")                 //.and().exceptionHandling().accessDeniedPage("/Access_Denied");                 //all the path need authentication                 .anyRequest().authenticated()                 .and() //Login Form configuration for all others             .formLogin()                 .loginPage("/login")                 //important because otherwise it goes in a loop because login page require authentication and authentication require login page                     .permitAll()             .and()             .logout()                 .logoutSuccessUrl("/login?logout")                 .permitAll();              // CSRF tokens handling         }     } 

MyUserDetailsService class

@Service("userDetailsService") public class MyUserDetailsService implements UserDetailsService {      @Autowired     private UserServices userServices;     static final Logger LOG = LoggerFactory.getLogger(MyUserDetailsService.class);      @Transactional(readOnly=true)     @Override     public UserDetails loadUserByUsername(final String username){         try{             com.domain.User user = userServices.findById(username);             if (user==null)                 LOG.error("Threw exception in MyUserDetailsService::loadUserByUsername : User doesn't exist" );              else{                 List<GrantedAuthority> authorities = buildUserAuthority(user.getUserRole());                 return buildUserForAuthentication(user, authorities);             }         }catch(Exception e){             LOG.error("Threw exception in MyUserDetailsService::loadUserByUsername : " + ErrorExceptionBuilder.buildErrorResponse(e));  }         return null;     }      // Converts com.users.model.User user to     // org.springframework.security.core.userdetails.User     private User buildUserForAuthentication(com.domain.User user, List<GrantedAuthority> authorities) {         return new User(user.getUsername(), user.getPassword(), user.isEnabled(), true, true, true, authorities);     }      private List<GrantedAuthority> buildUserAuthority(Set<UserRole> userRoles) {          Set<GrantedAuthority> setAuths = new HashSet<GrantedAuthority>();          // Build user's authorities         for (UserRole userRole : userRoles) {             setAuths.add(new SimpleGrantedAuthority(userRole.getUserRoleKeys().getRole()));         }          List<GrantedAuthority> Result = new ArrayList<GrantedAuthority>(setAuths);          return Result;     } 

so I have to:

1)access of user from login page for web pages and username and password for web services. This has to be done through Ldap.

2)the username of user needs for database query to authenticate user. Do you have any idea how I can implement this? Thanks

UPDATE WITH RIGHT CODE: Following the @M. Deinum advice I create MyAuthoritiesPopulator class instead of MyUserDetailsService and authentication with database and Ldap works:

    @Service("myAuthPopulator") public class MyAuthoritiesPopulator implements LdapAuthoritiesPopulator {      @Autowired     private UserServices userServices;     static final Logger LOG = LoggerFactory.getLogger(MyAuthoritiesPopulator.class);      @Transactional(readOnly=true)     @Override     public Collection<? extends GrantedAuthority> getGrantedAuthorities(DirContextOperations userData, String username) {         Set<GrantedAuthority> authorities = new HashSet<GrantedAuthority>();         try{             com.domain.User user = userServices.findById(username);             if (user==null)                 LOG.error("Threw exception in MyAuthoritiesPopulator::getGrantedAuthorities : User doesn't exist into ATS database" );               else{                 for(UserRole userRole : user.getUserRole()) {                     authorities.add(new SimpleGrantedAuthority(userRole.getUserRoleKeys().getRole()));                 }                 return authorities;             }         }catch(Exception e){             LOG.error("Threw exception in MyAuthoritiesPopulator::getGrantedAuthorities : " + ErrorExceptionBuilder.buildErrorResponse(e)); }         return authorities;     } } 

and I changed SecurityConfig as below:

@Configuration @EnableWebSecurity @EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true, proxyTargetClass = true) public class SecurityConfig extends WebSecurityConfigurerAdapter {      @Autowired     @Qualifier("myAuthPopulator")     LdapAuthoritiesPopulator myAuthPopulator;      @Autowired     public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {           auth.ldapAuthentication()           .contextSource()             .url("ldap://127.0.0.1:10389/dc=example,dc=com") //          .managerDn("") //          .managerPassword("")           .and()                .userSearchBase("ou=people")             .userSearchFilter("(uid={0})")             .ldapAuthoritiesPopulator(myAuthPopulator);          }      @Configuration     @Order(1)     public static class ApiWebSecurityConfig extends WebSecurityConfigurerAdapter{         @Override         protected void configure(HttpSecurity http) throws Exception {              http.csrf().disable()              .antMatcher("/client/**")              .authorizeRequests()              //Excluede send file from authentication because it doesn't work with spring authentication              //TODO add java authentication to send method              .antMatchers(HttpMethod.POST, "/client/file").permitAll()              .anyRequest().authenticated()              .and()              .httpBasic();         }     }      @Configuration     @Order(2)     public static class FormWebSecurityConfig extends WebSecurityConfigurerAdapter{          @Override         public void configure(WebSecurity web) throws Exception {             web             //Spring Security ignores request to static resources such as CSS or JS files.             .ignoring()             .antMatchers("/static/**");         }          @Override         protected void configure(HttpSecurity http) throws Exception {             http             .authorizeRequests() //Authorize Request Configuration                 //the "/" and "/register" path are accepted without login                 //.antMatchers("/", "/register").permitAll()                 //the /acquisition/** need admin role                 //.antMatchers("/acquisition/**").hasRole("ADMIN")                 //.and().exceptionHandling().accessDeniedPage("/Access_Denied");                 //all the path need authentication                 .anyRequest().authenticated()                 .and() //Login Form configuration for all others             .formLogin()                 .loginPage("/login")                 //important because otherwise it goes in a loop because login page require authentication and authentication require login page                     .permitAll()             .and()             .logout()                 .logoutSuccessUrl("/login?logout")                 .permitAll();         }     } } 

My LDAP development environment created in Apache directory studio

ldap

like image 503
luca Avatar asked Jan 07 '16 15:01

luca


People also ask

How does spring boot integrate with LDAP?

Spring Boot provides auto-configuration for an embedded server written in pure Java, which is being used for this guide. The ldapAuthentication() method configures things so that the user name at the login form is plugged into {0} such that it searches uid={0},ou=people,dc=springframework,dc=org in the LDAP server.


1 Answers

Spring Security already supports LDAP out-of-the-box. It actually has a whole chapter on this.

To use and configure LDAP add the spring-security-ldap dependency and next use the AuthenticationManagerBuilder.ldapAuthentication to configure it. The LdapAuthenticationProviderConfigurer allows you to set the needed things up.

@Autowired public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {     auth.ldapAuthentication()       .contextSource()         .url(...)         .port(...)         .managerDn(...)         .managerPassword(...)       .and()         .passwordEncoder(passwordEncoder())         .userSearchBase(...)                 .ldapAuthoritiesPopulator(new UserServiceLdapAuthoritiesPopulater(this.userService));       } 

Something like that (it should give you at least an idea on what/how to configure things) there are more options but check the javadocs for that. If you cannot use the UserService as is to retrieve the roles (because only the roles are in the database) then implement your own LdapAuthoritiesPopulator for that.

like image 102
M. Deinum Avatar answered Sep 23 '22 13:09

M. Deinum