我的应用程序具有一个安全配置,该配置通过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;
}
}
可悲的是,我能够登录(访问没有像我预期的那样被拒绝)。我错过了什么吗?
答案 0 :(得分:4)
Spring不会使用多个AuthenticationProvider
来认证请求,因此第一个(在ArrayList
中)AuthenticationProvider
支持Authentication
对象并成功进行认证该请求将是唯一使用的请求。在您的情况下为activeDirectoryLdapAuthenticationProvider
。
代替使用ActiveDirectoryLdapAuthenticationProvider
,您可以使用委托给LDAP并执行其他检查的自定义AuthenticationProvider:
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
}
}
答案 1 :(得分:1)
作为示例,可以使用多种身份验证机制: 找到代码
@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 config中配置多个身份验证提供程序。
@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方法中的objectPostProcessor需要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;
}
});
}
答案 2 :(得分:1)
这不是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
。这样,正常流程仍然适用,您可以重用大多数现有基础结构。