代码之家  ›  专栏  ›  技术社区  ›  Rüdiger

连接多个身份验证机制Spring引导安全性

  •  0
  • Rüdiger  · 技术社区  · 6 年前

    我的应用程序有一个安全配置,可以通过 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 回复  |  直到 6 年前
        1
  •  3
  •   stacker    6 年前

    弹簧最多用一个 AuthenticationProvider 为了验证请求,所以第一个 ArrayList ) 身份验证提供程序 支持 Authentication 对象并成功验证请求将是唯一使用的请求。就你而言 activeDirectoryLdapAuthenticationProvider .

    而不是使用 ActiveDirectoryLdapAuthenticationProvider ,可以使用委托给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
            }
    
        }
    
        2
  •  1
  •   munipratap Abdel    6 年前

    作为多身份验证机制的示例: 查找代码

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

    configure方法中的objectprocessor需要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;
            }
        });
    }
    
        3
  •  1
  •   M. Deinum    6 年前

    这不是 AuthenticationProvider 有效,仅查询一个进行身份验证。显然,您希望结合来自LDAP和DB的一些信息。为此,您可以配置自定义 UserDetailsContextMapper 和/或 GrantedAuthoritiesMapper . 默认实现将使用来自LDAP的信息来构造 UserDetails 及其 GrantedAuthorities 但是,您可以实现一个查询数据库的策略。

    另一个解决方案是使用 LdapUserDetailsService 它允许你使用普通的 DaoAuthenticationProvider . 该名称具有误导性,因为它实际上需要 UserDetailsService . 这个 身份验证提供程序 是否使用 UserDetailsChecker ,默认情况下会检查 用户详细信息 ,但可以通过您的附加支票进行扩展。

    注: 这个 ldapuser详细信息服务 使用普通LDAP,因此我不知道它是否适用于稍有不同的Active Directory方法!

    最后的解决方案可能是创建一个 DelegatingAuthenticationProvider 延伸自 AbstractUserDetailsAuthenticationProvider 这样您就可以重用其中的逻辑来利用 用户详细信息检查程序 . 这个 retrieveUser 然后方法将委托给 ActiveDirectoryLdapAuthenticationProvider 进行身份验证。

    注: 而不是扩展 抽象用户详细信息身份验证提供程序 当然,您也可以自己创建一个更简单的版本。

    总之,我怀疑 用户详细信息上下文映射器 最简单,如果在数据库中找不到,则抛出 UsernameNotFoundException . 这样,正常的流程仍然适用,您可以重用大部分现有的基础设施。