Подключение нескольких механизмов аутентификации Spring Boot Security - PullRequest
0 голосов
/ 24 января 2019

У меня есть конфигурация безопасности для моего приложения, которая аутентифицирует пользователя через LDAP. Это работает довольно хорошо, но теперь я хотел бы добавить еще один AuthenticationProvider, который делает еще несколько проверок для пользователя, который пытается аутентифицироваться. Поэтому я попытался добавить DbAuthenticationProvider, который (для целей тестирования) всегда запрещает доступ. Поэтому, когда я пытаюсь войти в систему с моей учетной записью домена (которая работает для activeDirectoryLdapAuthenticationProvider), я не могу получить доступ к странице, потому что второй поставщик не проходит проверку подлинности.

Для достижения этой цели я использовал следующий код:

@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Value("${ad.domain}")
    private String AD_DOMAIN;

    @Value("${ad.url}")
    private String AD_URL;

    @Autowired
    UserRoleComponent userRoleComponent;

    @Autowired
    DbAuthenticationProvider dbAuthenticationProvider;

    private final Logger logger = LoggerFactory.getLogger(WebSecurityConfig.class);

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        this.logger.info("Verify logging level");
        http.authorizeRequests().anyRequest().fullyAuthenticated().and().formLogin()
                .successHandler(new CustomAuthenticationSuccessHandler()).and().httpBasic().and().logout()
                .logoutUrl("/logout").invalidateHttpSession(true).deleteCookies("JSESSIONID");
        http.formLogin().defaultSuccessUrl("/", true);
    }


    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.authenticationProvider(activeDirectoryLdapAuthenticationProvider());
        auth.authenticationProvider(dbAuthenticationProvider);
    }

    @Bean
    public AuthenticationManager authenticationManager() {
        return new ProviderManager(Arrays.asList(activeDirectoryLdapAuthenticationProvider(), dbAuthenticationProvider));
    }

    @Bean
    public AuthenticationProvider activeDirectoryLdapAuthenticationProvider() {
        ActiveDirectoryLdapAuthenticationProvider provider = new ActiveDirectoryLdapAuthenticationProvider(AD_DOMAIN,
                AD_URL);
        provider.setConvertSubErrorCodesToExceptions(true);
        provider.setUseAuthenticationRequestCredentials(true);
        return provider;
    }
}

А это мой DbAuthenticationProvider:

@Component
public class DbAuthenticationProvider implements AuthenticationProvider {

    Logger logger = LoggerFactory.getLogger(DbAuthenticationProvider.class);

    @Override
    public Authentication authenticate(Authentication auth) throws AuthenticationException {
        auth.setAuthenticated(false);
        this.logger.info("Got initialized");
        return auth;
    }

    @Override
    public boolean supports(Class<?> authentication) {
        return true;
    }

}

К сожалению, я могу войти в систему (доступ не запрещен, как я ожидал). Я что-то упустил?

Ответы [ 3 ]

0 голосов
/ 29 января 2019

Spring Не будет использовать более одного AuthenticationProvider для аутентификации запроса, поэтому будет использоваться только один (в ArrayList) AuthenticationProvider, который поддерживает объект Authentication и успешно аутентифицирует запрос. , в вашем случае это activeDirectoryLdapAuthenticationProvider.

вместо ActiveDirectoryLdapAuthenticationProvider, вы можете использовать пользовательский AuthenticationProvider, который делегирует LDAP и выполняет дополнительные проверки:

    CustomerAuthenticationProvider implements AuthenticationProvider{
        privtae ActiveDirectoryLdapAuthenticationProvider  delegate; // add additional methods to initialize delegate during your configuration

          @Override
         public Authentication authenticate(Authentication auth) throws 
             AuthenticationException {
            Authentication  authentication= delegate.authenticate(auth);
            additionalChecks(authentication);
           return auth;
           }


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

        public void additionalCheck(Authentication authentication){
               // throw AuthenticationException when it's not allowed
        }

    }
0 голосов
/ 29 января 2019

Это не то, как AuthenticationProvider работает, только один будет проверен для аутентификации.Видимо, вы хотите объединить некоторую информацию из LDAP и из БД.Для этого вы можете настроить пользовательский UserDetailsContextMapper и / или GrantedAuthoritiesMapper.Реализация по умолчанию будет использовать информацию из LDAP для построения UserDetails и его GrantedAuthorities, однако вы можете реализовать стратегию, которая обращается к базе данных.

Другое решение заключается в использовании LdapUserDetailsService, который позволяет использовать обычный DaoAuthenticationProvider.Имя вводит в заблуждение, поскольку на самом деле требуется UserDetailsService.Этот AuthenticationProvider выполняет дополнительные проверки, используя UserDetailsChecker, который по умолчанию проверяет некоторые свойства UserDetails, но может быть расширен за счет дополнительных проверок.

ПРИМЕЧАНИЕ: LdapUserDetailsService использует простой LDAP, поэтому я не знаю, применимо ли это к немного другому подходу Active Directory!

Окончательное решение может бытьсоздать DelegatingAuthenticationProvider, который простирается от AbstractUserDetailsAuthenticationProvider, так что вы можете использовать там логику для использования UserDetailsChecker.Затем метод retrieveUser делегировал бы фактическому ActiveDirectoryLdapAuthenticationProvider для выполнения аутентификации.

ПРИМЕЧАНИЕ: Вместо расширения AbstractUserDetailsAuthenticationProvider вы, конечно, можете также создать более простую версию самостоятельно.

В общем, я подозреваю, что создание настроенного UserDetailsContextMapper было бы самым простым, и, если его не найти в БД, выведите UsernameNotFoundException.Таким образом, нормальный поток все еще применяется, и вы можете повторно использовать большую часть существующей инфраструктуры.

0 голосов
/ 29 января 2019

В качестве примера можно обойти механизм множественной аутентификации: найти код

@Configuration
@EnableWebSecurity
@Profile("container")
public class CustomWebSecurityConfig  extends WebSecurityConfigurerAdapter {

@Autowired
private AuthenticationProvider authenticationProvider;

@Autowired
private AuthenticationProvider authenticationProviderDB;

@Override
@Order(1)
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    auth.authenticationProvider(authenticationProvider);
}

@Order(2)
protected void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
    auth.authenticationProvider(authenticationProviderDB);
}

@Override
  public void configure(WebSecurity web) throws Exception {
    web
      .ignoring()
         .antMatchers("/scripts/**","/styles/**","/images/**","/error/**");
  }

@Override
public void configure(HttpSecurity http) throws Exception {
    http
            .authorizeRequests()
            .antMatchers("/rest/**").authenticated()
            .antMatchers("/**").permitAll()
            .anyRequest().authenticated()
            .and()
            .formLogin()
            .successHandler(new AuthenticationSuccessHandler() {
                @Override
                public void onAuthenticationSuccess(
                        HttpServletRequest request,
                        HttpServletResponse response,
                        Authentication a) throws IOException, ServletException {
                            //To change body of generated methods,
                            response.setStatus(HttpServletResponse.SC_OK);
                        }
            })
            .failureHandler(new AuthenticationFailureHandler() {

                @Override
                public void onAuthenticationFailure(
                        HttpServletRequest request,
                        HttpServletResponse response,
                        AuthenticationException ae) throws IOException, ServletException {
                            response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
                        }
            })
            .loginProcessingUrl("/access/login")
            .and()
            .logout()
            .logoutUrl("/access/logout")                
            .logoutSuccessHandler(new LogoutSuccessHandler() {
                @Override
                public void onLogoutSuccess(
                        HttpServletRequest request, 
                        HttpServletResponse response, 
                        Authentication a) throws IOException, ServletException {
                    response.setStatus(HttpServletResponse.SC_NO_CONTENT);
                }
            })
            .invalidateHttpSession(true)
            .and()
            .exceptionHandling()
            .authenticationEntryPoint(new Http403ForbiddenEntryPoint())
            .and()
            .csrf()//Disabled CSRF protection
            .disable();
    }
} 

настроил два провайдера аутентификации в Spring Security

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

конфигурация, которая помогает настроить несколько провайдеров аутентификации в конфигурации Java.

@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
    auth.authenticationProvider(authenticationProvider);
    auth.authenticationProvider(DBauthenticationProvider);
}


@Configuration
@EnableWebSecurity
public class XSecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private LDAPAuthenticationProvider authenticationProvider;

    @Autowired
    private DBAuthenticationProvider dbauthenticationProvider;

    @Override
      public void configure(WebSecurity web) throws Exception {
        web
          .ignoring()
             .antMatchers("/scripts/**","/styles/**","/images/**","/error/**");
      }

    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
        auth.authenticationProvider(authenticationProvider);
        auth.authenticationProvider(dbauthenticationProvider);

    }


    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable()
        .authorizeRequests()
            .antMatchers("/","/logout").permitAll()
            .antMatchers("/admin").hasRole("ADMIN")         
            .anyRequest().authenticated()
            .and()
        .formLogin()
            .loginPage("/index")
            .loginProcessingUrl("/perform_login")
            .usernameParameter("user")
            .passwordParameter("password")
            .failureUrl("/index?failed=true")
            .defaultSuccessUrl("/test",true)
            .permitAll()
            .and()
         .logout().logoutUrl("/logout")
                  .logoutSuccessUrl("/index?logout=true").permitAll()
            .and()
            .exceptionHandling().accessDeniedPage("/error");
    }

}

objectPostProcessor внутри метода configure требует AuthenticationManagerBuilder для фактического построения объекта, прежде чем мы сможем получить доступ и изменить порядок поставщиков

@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
      auth.jdbcAuthentication().dataSource(dataSource)
           .passwordEncoder(new BCryptPasswordEncoder());
      auth.authenticationProvider(new CustomAuthenticationProvider(this.dataSource));

      auth.objectPostProcessor(new ObjectPostProcessor<Object>() {
        @Override
        public <O> O postProcess(O object) {
            ProviderManager providerManager = (ProviderManager) object;
            Collections.swap(providerManager.getProviders(), 0, 1);
            return object;
        }
    });
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...