赞
踩
MyDaoAuthenticationProvider重写DaoAuthenticationProvider中additionalAuthenticationChecks方法后,自定义验证密码错误后,抛出BadCredentialsException异常,代码会继续执行,会执行DaoAuthenticationProvider中的additionalAuthenticationChecks发放,导致自定义抛出的BadCredentialsException的异常无用。
public class MyDaoAuthenticationProvider extends DaoAuthenticationProvider { private static final Logger log = LoggerFactory.getLogger(MyDaoAuthenticationProvider.class); @SneakyThrows @Override protected void additionalAuthenticationChecks(UserDetails userDetails, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException { if (authentication.getCredentials() == null) { logger.debug("Authentication failed: no credentials provided"); throw new BadCredentialsException(messages.getMessage( "AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials")); } String presentedPassword = authentication.getCredentials().toString(); if (!iscLoginJsoupReq(userDetails.getUsername(), presentedPassword)) { this.logger.debug("Failed to authenticate since password does not match stored value"); throw new BadCredentialsException(this.messages .getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials")); } } }
在debug中,发现ProviderManager类中authenticate方法中,出现以下代码片段:
for (AuthenticationProvider provider : getProviders()) { if (!provider.supports(toTest)) { continue; } if (logger.isTraceEnabled()) { logger.trace(LogMessage.format("Authenticating request with %s (%d/%d)", provider.getClass().getSimpleName(), ++currentPosition, size)); } try { result = provider.authenticate(authentication); if (result != null) { copyDetails(authentication, result); break; } } catch (AccountStatusException | InternalAuthenticationServiceException ex) { prepareException(ex, authentication); // SEC-546: Avoid polling additional providers if auth failure is due to // invalid account status throw ex; } catch (AuthenticationException ex) { lastException = ex; } }
在getProviders()方法中,会获取到两个AuthenticationProvider,一个是自定义的MyDaoAuthenticationProvider,一个是默认的DaoAuthenticationProvider,在执行DaoAuthenticationProvider中的additionalAuthenticationChecks方法时,就会验证密码成功。
在继承WebSecurityConfigurerAdapter的配置类中,在以下方法中:
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
ApplicationContext applicationContext = getApplicationContext();
ConfigurableEnvironment environment = (ConfigurableEnvironment) applicationContext.getEnvironment();
String name = environment.getProperty("spring.profiles.active");
if (ACTIVE.equals(name)) {
auth.authenticationProvider(myProvider());
} else {
auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder());
}
}
auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder());
这句代码会生成一个DaoAuthenticationProvider在容器中供使用,且容器中定义了几个Provider,在getProviders()
方法中就会获取到几个,并循环执行。所以在使用自定义的DaoAuthenticationProvider是,需要注释auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder());
此代码片段。在不使用自定义的Provider时,需放开此代码片段。
DaoAuthenticationProvider和MyDaoAuthenticationProvider其实是功能一模一样的两个列,除了重写的additionalAuthenticationChecks()
的实现类不一样,在执行DaoAuthenticationProvider中的additionalAuthenticationChecks时,会得到正确的result进行返回,在代码中,只要得到验证通过,得到正确的result,就会进行返回,代码如下:
@Override public Authentication authenticate(Authentication authentication) throws AuthenticationException { Class<? extends Authentication> toTest = authentication.getClass(); AuthenticationException lastException = null; AuthenticationException parentException = null; Authentication result = null; Authentication parentResult = null; int currentPosition = 0; int size = this.providers.size(); for (AuthenticationProvider provider : getProviders()) { if (!provider.supports(toTest)) { continue; } if (logger.isTraceEnabled()) { logger.trace(LogMessage.format("Authenticating request with %s (%d/%d)", provider.getClass().getSimpleName(), ++currentPosition, size)); } try { result = provider.authenticate(authentication); if (result != null) { copyDetails(authentication, result); break; } } catch (AccountStatusException | InternalAuthenticationServiceException ex) { prepareException(ex, authentication); // SEC-546: Avoid polling additional providers if auth failure is due to // invalid account status throw ex; } catch (AuthenticationException ex) { lastException = ex; } } if (result == null && this.parent != null) { // Allow the parent to try. try { parentResult = this.parent.authenticate(authentication); result = parentResult; } catch (ProviderNotFoundException ex) { // ignore as we will throw below if no other exception occurred prior to // calling parent and the parent // may throw ProviderNotFound even though a provider in the child already // handled the request } catch (AuthenticationException ex) { parentException = ex; lastException = ex; } } if (result != null) { if (this.eraseCredentialsAfterAuthentication && (result instanceof CredentialsContainer)) { // Authentication is complete. Remove credentials and other secret data // from authentication ((CredentialsContainer) result).eraseCredentials(); } // If the parent AuthenticationManager was attempted and successful then it // will publish an AuthenticationSuccessEvent // This check prevents a duplicate AuthenticationSuccessEvent if the parent // AuthenticationManager already published it if (parentResult == null) { this.eventPublisher.publishAuthenticationSuccess(result); } return result; } // Parent was null, or didn't authenticate (or throw an exception). if (lastException == null) { lastException = new ProviderNotFoundException(this.messages.getMessage("ProviderManager.providerNotFound", new Object[] { toTest.getName() }, "No AuthenticationProvider found for {0}")); } // If the parent AuthenticationManager was attempted and failed then it will // publish an AbstractAuthenticationFailureEvent // This check prevents a duplicate AbstractAuthenticationFailureEvent if the // parent AuthenticationManager already published it if (parentException == null) { prepareException(lastException, authentication); } throw lastException; }
在执行DaoAuthenticationProvider中的result = provider.authenticate(authentication);
时,会得到验证通过的result,在下面代码会返回result:
if (result != null) { if (this.eraseCredentialsAfterAuthentication && (result instanceof CredentialsContainer)) { // Authentication is complete. Remove credentials and other secret data // from authentication ((CredentialsContainer) result).eraseCredentials(); } // If the parent AuthenticationManager was attempted and successful then it // will publish an AuthenticationSuccessEvent // This check prevents a duplicate AuthenticationSuccessEvent if the parent // AuthenticationManager already published it if (parentResult == null) { this.eventPublisher.publishAuthenticationSuccess(result); } return result; }
所以不会再代码最后一行,执行抛出throw lastException;
这一行代码。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。