Java – spring securely switches to LDAP authentication and database permissions
I implemented database authentication for my web pages and web services
@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,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) The user accesses the user from the login page of the web page and the user name and password of the web service This must be done through LDAP
2) The user needs the user name to query the database to authenticate the user Do you have any ideas that can be realized? thank you
Update with the correct code: follow @ M. deinum's recommendation that I create the myauthoritiespopulator class instead of myuserdetailsservice and use the database and LDAP for authentication:
@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; } }
I changed the securityconfig as follows:
@Configuration @EnableWebSecurity @EnableGlobalMethodSecurity(securedEnabled = 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 was created in the Apache directory studio
Solution
Spring security already supports LDAP out of the box It actually has a whole chapter
To use and configure LDAP, add the spring security LDAP dependency, and then use authenticationmanagerbuilder LDAP authentication is configured Ldapauthenticationproviderconfigurer allows you to set up what you need
@Autowired public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { auth.ldapAuthentication() .contextSource() .url(...) .port(...) .managerDn(...) .managerPassword(...) .and() .passwordEncoder(passwordEncoder()) .userSearchBase(...) .ldapAuthoritiesPopulator(new UserServiceLdapAuthoritiesPopulater(this.userService)); }
Such things (which should give you at least an idea of what / how to configure things) have more choices, but check JavaDocs If you cannot use userservice to retrieve roles (because only roles are in the database), you can implement your own ldapauthorities populator