web-dev-qa-db-ger.com

Frühjahrssicherheitswechsel auf Ldap-Authentifizierung und Datenbankberechtigungen

Ich habe die Datenbankauthentifizierung für meine Webseite und meinen Web-Service implementiert .. __ Es funktioniert gut für beide. Jetzt muss ich die Ldap-Authentifizierung hinzufügen. __ existiert Ich muss meine Datenbank für Benutzerrollen verwenden (in meinem Datenbankbenutzernamen ist derselbe Benutzername wie Ldap) . Daher muss ich von meinem eigentlichen Code zur Ldap- und Datenbankauthentifizierung wechseln, wie oben erläutert. Mein Code lautet: SecurityConfig-Klasse

@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-Klasse

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

also muss ich:

1) Zugriff des Benutzers von der Anmeldeseite für Webseiten sowie Benutzername und Kennwort für Webdienste. Dies muss über Ldap erfolgen.

2) Der Benutzername des Benutzers benötigt eine Datenbankabfrage, um den Benutzer zu authentifizieren .. _ Haben Sie eine Idee, wie ich dies implementieren kann? Vielen Dank

UPDATE WITH RIGHT CODE: Nach dem @M. Deinum Ratschlag Ich erstelle MyAuthoritiesPopulator Klasse anstelle von MyUserDetailsService und Authentifizierung mit Datenbank und Ldap funktioniert:

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

und ich habe SecurityConfig wie folgt geändert:

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

Meine in Apache Directory Studio erstellte LDAP-Entwicklungsumgebung

 ldap

17
luca

Spring Security unterstützt bereits LDAP Out-of-the-Box. Es hat eigentlich ein ganzes Kapitel dazu.

Um LDAP zu verwenden und zu konfigurieren, fügen Sie die spring-security-ldap-Abhängigkeit hinzu und verwenden Sie als Nächstes den AuthenticationManagerBuilder.ldapAuthentication , um diese zu konfigurieren. Mit der LdapAuthenticationProviderConfigurer können Sie die benötigten Dinge einrichten. 

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

So etwas (es sollte Ihnen zumindest eine Vorstellung davon geben, was/wie man Dinge konfiguriert). Es gibt mehr Optionen, aber überprüfen Sie die Javadocs. Wenn Sie die UserService nicht verwenden können, um die Rollen abzurufen (da sich nur die Rollen in der Datenbank befinden), implementieren Sie Ihre eigene LdapAuthoritiesPopulator .

7
M. Deinum

Sie müssen eineCustomAuthenticationProviderwichAuthenticationProvidererstellen unddie authenticate-Methode überschreiben. Beispiel: 

@Component
public class CustomAuthenticationProvider
    implements AuthenticationProvider {

    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        String username = authentication.getName();
        String password = authentication.getCredentials().toString();

        boolean authenticated = false;
        /**
         * Here implements the LDAP authentication
         * and return authenticated for example
         */
        if (authenticated) {

            String usernameInDB = "";
            /**
             * Here look for username in your database!
             * 
             */
            List<GrantedAuthority> grantedAuths = new ArrayList<>();
            grantedAuths.add(new     SimpleGrantedAuthority("ROLE_USER"));
            Authentication auth = new     UsernamePasswordAuthenticationToken(usernameInDB, password,     grantedAuths);
            return auth;
        } else {
            return null;
        }
    }

    @Override
    public boolean supports(Class<?> authentication) {
        return     authentication.equals(UsernamePasswordAuthenticationToken.class);
    }

}

Dann müssen Sie in IhrerSecurityConfigdas configurethats use AuthenticationManagerBuilder überschreiben.

@Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
    auth.authenticationProvider(this.authenticationProvider);
}

Sie können den CustomAuthenticationProvider auf diese Weise automatisch aktivieren:

@Autowired
private CustomAuthenticationProvider authenticationProvider;

Auf diese Weise können Sie das Standardauthentifizierungsverhalten überschreiben.

1
melli-182

Für jeden, der Grills benutzt, ist es viel einfacher. Fügen Sie dies einfach Ihrer Konfiguration hinzu:

grails: Plugin: Federsicherheit: ldap: Behörden: retrieveDatabaseRoles: true

0
rhinmass

Ich fand auch dieses Kapitel Spring Docu Custom Authenicator und baute meinen eigenen Schalter zwischen LDAP und meinen DB-Benutzern auf. Ich kann mühelos zwischen den Anmeldedaten mit festgelegten Prioritäten wechseln (in meinem Fall gewinnt LDAP).

Ich habe ein LDAP mit den yaml-Konfigurationsdateien für die LDAP-Benutzerdaten konfiguriert, die ich hier nicht im Detail offenlege. Dies kann leicht mit dieser Spring Docu LDAP-Konfiguration durchgeführt werden.

Ich habe das folgende Beispiel vom Klappern wie Logger/Javadoc usw. entfernt, um die wichtigen Teile hervorzuheben. Die Annotation @Order bestimmt die Prioritäten, in denen die Login-Daten verwendet werden. Die Details im Arbeitsspeicher sind nur für dev-Zwecke hardcodiert.

SecurityWebConfiguration

@Configuration
@EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {

  @Inject
  private Environment env;
  @Inject
  private LdapConfiguration ldapConfiguration;

  @Inject
  private BaseLdapPathContextSource contextSource;
  @Inject
  private UserDetailsContextMapper userDetailsContextMapper;

  @Inject
  private DBAuthenticationProvider dbLogin;

  @Inject
  @Order(10) // the lowest number wins and is used first
  public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
    auth.userDetailsService(new InMemoryUserDetailsManager(getInMemoryUserDetails()));
  }

  @Inject
  @Order(11) // the lowest number wins and is used first
  public void configureLDAP(AuthenticationManagerBuilder auth) throws Exception {
    if (ldapConfiguration.isLdapEnabled()) {
      auth.ldapAuthentication().userSearchBase(ldapConfiguration.getUserSearchBase())
          .userSearchFilter(ldapConfiguration.getUserSearchFilter())
          .groupSearchBase(ldapConfiguration.getGroupSearchBase()).contextSource(contextSource)
          .userDetailsContextMapper(userDetailsContextMapper);
    }
  }

  @Inject
  @Order(12) // the lowest number wins and is used first
  public void configureDB(AuthenticationManagerBuilder auth) throws Exception {
    auth.authenticationProvider(dbLogin);
  }
}

DB Authenticator

@Component
public class DBAuthenticationProvider implements AuthenticationProvider {

  @Override
  public Authentication authenticate(Authentication authentication) throws AuthenticationException {
    String name = authentication.getName();
    String password = authentication.getCredentials().toString();

   // your code to compare to your DB
  }

  @Override
  public boolean supports(Class<?> authentication) {
    return authentication.equals(UsernamePasswordAuthenticationToken.class);
  }

  /**
   * @param original <i>mandatory</i> - input to be hashed with SHA256 and HEX encoding
   * @return the hashed input
   */
  private String sha256(String original) {
    MessageDigest md = null;
    try {
      md = MessageDigest.getInstance("SHA-256");
    } catch (NoSuchAlgorithmException e) {
      throw new AuthException("The processing of your password failed. Contact support.");
    }

    if (false == Strings.isNullOrEmpty(original)) {
      md.update(original.getBytes());
    }

    byte[] digest = md.digest();
    return new String(Hex.encodeHexString(digest));
  }

  private class AuthException extends AuthenticationException {
    public AuthException(final String msg) {
      super(msg);
    }
  }
}

Fühlen Sie sich frei, um Details zu fragen. Ich hoffe, das ist für jemanden nützlich: D

0
Dr4gon