当前位置:   article > 正文

Spring Security中的DaoAuthenticationProvider

daoauthenticationprovider

上一篇博客中已经了解了,认证的核心是各种AuthenticationProvider,这篇博客就来了解一下其中使用的最多的一个DaoAuthenticationProvider。

在此之前,先来了解一下三个类。UserDetails、UserDetailsService以及PasswordEncoder。

通俗的说,UserDetails是用户信息的实体类,UserDetailsService自定义实现,根据用户名密码加载UserDetails,PasswordEncoder就是密码的加密类。

看图,DaoAuthenticationProvider通过UserDetailsService以及PasswordEncoder,将用户名密码替换成UserDetails和Authorities。

接着看一看这个DaoAuthenticationProvider是从哪里配置来的,这个得从WebSecurityConfigurerAdapter说起。

在WebSecurityConfigurerAdapter中,以下代码之前都看过,AuthenticationManager 的由来也都说明过,其中有一个parent被所有的子AuthenticationManager共享,而这个DaoAuthenticationProvider就是包括在parent中。

  1. protected final HttpSecurity getHttp() throws Exception {
  2. if (http != null) {
  3. return http;
  4. }
  5. //获取AuthenticationEventPublisher
  6. AuthenticationEventPublisher eventPublisher = getAuthenticationEventPublisher();
  7. localConfigureAuthenticationBldr.authenticationEventPublisher(eventPublisher);
  8. //配置parent的AuthenticationManager,可覆盖实现自定义方法
  9. AuthenticationManager authenticationManager = authenticationManager();
  10. authenticationBuilder.parentAuthenticationManager(authenticationManager);
  11. Map<Class<?>, Object> sharedObjects = createSharedObjects();
  12. //新建HttpSecurity,并构建一个默认的HttpSecurity
  13. http = new HttpSecurity(objectPostProcessor, authenticationBuilder,
  14. sharedObjects);
  15. if (!disableDefaults) {
  16. // @formatter:off
  17. http
  18. .csrf().and()
  19. .addFilter(new WebAsyncManagerIntegrationFilter())
  20. .exceptionHandling().and()
  21. .headers().and()
  22. .sessionManagement().and()
  23. .securityContext().and()
  24. .requestCache().and()
  25. .anonymous().and()
  26. .servletApi().and()
  27. .apply(new DefaultLoginPageConfigurer<>()).and()
  28. .logout();
  29. // @formatter:on
  30. ClassLoader classLoader = this.context.getClassLoader();
  31. //spi 加载 AbstractHttpConfigurer的实现类,加入HttpSecurity中
  32. List<AbstractHttpConfigurer> defaultHttpConfigurers =
  33. SpringFactoriesLoader.loadFactories(AbstractHttpConfigurer.class, classLoader);
  34. for (AbstractHttpConfigurer configurer : defaultHttpConfigurers) {
  35. http.apply(configurer);
  36. }
  37. }
  38. //配置HttpSecurity,可自定义实现
  39. configure(http);
  40. return http;
  41. }
  1. protected AuthenticationManager authenticationManager() throws Exception {
  2. //如果没有初始化过,执行方法
  3. if (!authenticationManagerInitialized) {
  4. //configure(AuthenticationManagerBuilder auth)
  5. //交给子类自定义
  6. configure(localConfigureAuthenticationBldr);
  7. //如果没有自定义过
  8. if (disableLocalConfigureAuthenticationBldr) {
  9. //使用默认的
  10. authenticationManager = authenticationConfiguration
  11. .getAuthenticationManager();
  12. }
  13. else {
  14. //否则使用自定义过的进行构建
  15. authenticationManager = localConfigureAuthenticationBldr.build();
  16. }
  17. authenticationManagerInitialized = true;
  18. }
  19. return authenticationManager;
  20. }

以上代码可以看出,这个parent如果不自定义的话,会使用默认的authenticationConfiguration,而这个如下是spring注入进来的

  1. @Autowired
  2. public void setAuthenticationConfiguration(
  3. AuthenticationConfiguration authenticationConfiguration) {
  4. this.authenticationConfiguration = authenticationConfiguration;
  5. }

接下来再看AuthenticationConfiguration 是怎么加到spring容器中的。

还是@EnableWebSecurity注解,注解了一个@EnableGlobalAuthentication,这个注解import了一个AuthenticationConfiguration。

  1. //默认的情况获取AuthenticationManager
  2. public AuthenticationManager getAuthenticationManager() throws Exception {
  3. if (this.authenticationManagerInitialized) {
  4. return this.authenticationManager;
  5. }
  6. AuthenticationManagerBuilder authBuilder = this.applicationContext.getBean(AuthenticationManagerBuilder.class);
  7. if (this.buildingAuthenticationManager.getAndSet(true)) {
  8. return new AuthenticationManagerDelegator(authBuilder);
  9. }
  10. //放入三个configure,都在当前类中生成,GlobalAuthenticationConfigurerAdapter
  11. // InitializeUserDetailsBeanManagerConfigurer、InitializeAuthenticationProviderBeanManagerConfigurer
  12. for (GlobalAuthenticationConfigurerAdapter config : globalAuthConfigurers) {
  13. authBuilder.apply(config);
  14. }
  15. authenticationManager = authBuilder.build();
  16. if (authenticationManager == null) {
  17. authenticationManager = getAuthenticationManagerBean();
  18. }
  19. this.authenticationManagerInitialized = true;
  20. return authenticationManager;
  21. }

 而在这个AuthenticationConfiguration中,会生成如上的AuthenticationManager,这个就是parent。

而在这个AuthenticationManager种,设置了多个globalAuthConfigurers

  1. @Bean
  2. public static GlobalAuthenticationConfigurerAdapter enableGlobalAuthenticationAutowiredConfigurer(
  3. ApplicationContext context) {
  4. return new EnableGlobalAuthenticationAutowiredConfigurer(context);
  5. }
  6. @Bean
  7. public static InitializeUserDetailsBeanManagerConfigurer initializeUserDetailsBeanManagerConfigurer(ApplicationContext context) {
  8. return new InitializeUserDetailsBeanManagerConfigurer(context);
  9. }
  10. @Bean
  11. public static InitializeAuthenticationProviderBeanManagerConfigurer initializeAuthenticationProviderBeanManagerConfigurer(ApplicationContext context) {
  12. return new InitializeAuthenticationProviderBeanManagerConfigurer(context);
  13. }

可以看到,当前类中有这么三个Configurer,其中InitializeUserDetailsBeanManagerConfigurer中会生成DaoAuthenticationProvider,要注意的是,这里并不是在这个类中直接生成的,而是如下,先注入了一个InitializeUserDetailsManagerConfigurer,在InitializeUserDetailsManagerConfigurer中创建的。

Configurer的逻辑前几篇博客已经说明过了

  1. @Override
  2. public void init(AuthenticationManagerBuilder auth) throws Exception {
  3. auth.apply(new InitializeUserDetailsManagerConfigurer());
  4. }
  1. public void configure(AuthenticationManagerBuilder auth) throws Exception {
  2. if (auth.isConfigured()) {
  3. return;
  4. }
  5. //从spring中获取UserDetailsService,如果没有,直接返回
  6. //所以要执行下面的代码,必须要有UserDetailsService
  7. UserDetailsService userDetailsService = getBeanOrNull(
  8. UserDetailsService.class);
  9. if (userDetailsService == null) {
  10. return;
  11. }
  12. //从spring中获取PasswordEncoder
  13. PasswordEncoder passwordEncoder = getBeanOrNull(PasswordEncoder.class);
  14. UserDetailsPasswordService passwordManager = getBeanOrNull(UserDetailsPasswordService.class);
  15. //构建DaoAuthenticationProvider
  16. DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
  17. provider.setUserDetailsService(userDetailsService);
  18. if (passwordEncoder != null) {
  19. provider.setPasswordEncoder(passwordEncoder);
  20. }
  21. if (passwordManager != null) {
  22. provider.setUserDetailsPasswordService(passwordManager);
  23. }
  24. provider.afterPropertiesSet();
  25. //将provider放入AuthenticationManagerBuilder中
  26. auth.authenticationProvider(provider);
  27. }

到此,DaoAuthenticationProvider的由来就已经清晰了。

再看看逻辑,如下代码,逻辑和之前描述一致。

  1. protected final UserDetails retrieveUser(String username,
  2. UsernamePasswordAuthenticationToken authentication)
  3. throws AuthenticationException {
  4. prepareTimingAttackProtection();
  5. try {
  6. //调用UserDetailsService的loadUserByUsername方法,方法需要自定义
  7. UserDetails loadedUser = this.getUserDetailsService().loadUserByUsername(username);
  8. if (loadedUser == null) {
  9. throw new InternalAuthenticationServiceException(
  10. "UserDetailsService returned null, which is an interface contract violation");
  11. }
  12. return loadedUser;
  13. }
  14. catch (UsernameNotFoundException ex) {
  15. mitigateAgainstTimingAttack(authentication);
  16. throw ex;
  17. }
  18. catch (InternalAuthenticationServiceException ex) {
  19. throw ex;
  20. }
  21. catch (Exception ex) {
  22. throw new InternalAuthenticationServiceException(ex.getMessage(), ex);
  23. }
  24. }

结束!

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/weixin_40725706/article/detail/534048
推荐阅读
相关标签
  

闽ICP备14008679号