赞
踩
上一篇博客中已经了解了,认证的核心是各种AuthenticationProvider,这篇博客就来了解一下其中使用的最多的一个DaoAuthenticationProvider。
在此之前,先来了解一下三个类。UserDetails、UserDetailsService以及PasswordEncoder。
通俗的说,UserDetails是用户信息的实体类,UserDetailsService自定义实现,根据用户名密码加载UserDetails,PasswordEncoder就是密码的加密类。
看图,DaoAuthenticationProvider通过UserDetailsService以及PasswordEncoder,将用户名密码替换成UserDetails和Authorities。
接着看一看这个DaoAuthenticationProvider是从哪里配置来的,这个得从WebSecurityConfigurerAdapter说起。
在WebSecurityConfigurerAdapter中,以下代码之前都看过,AuthenticationManager 的由来也都说明过,其中有一个parent被所有的子AuthenticationManager共享,而这个DaoAuthenticationProvider就是包括在parent中。
- protected final HttpSecurity getHttp() throws Exception {
- if (http != null) {
- return http;
- }
-
- //获取AuthenticationEventPublisher
- AuthenticationEventPublisher eventPublisher = getAuthenticationEventPublisher();
- localConfigureAuthenticationBldr.authenticationEventPublisher(eventPublisher);
-
- //配置parent的AuthenticationManager,可覆盖实现自定义方法
- AuthenticationManager authenticationManager = authenticationManager();
- authenticationBuilder.parentAuthenticationManager(authenticationManager);
-
- Map<Class<?>, Object> sharedObjects = createSharedObjects();
-
- //新建HttpSecurity,并构建一个默认的HttpSecurity
- http = new HttpSecurity(objectPostProcessor, authenticationBuilder,
- sharedObjects);
- if (!disableDefaults) {
- // @formatter:off
- http
- .csrf().and()
- .addFilter(new WebAsyncManagerIntegrationFilter())
- .exceptionHandling().and()
- .headers().and()
- .sessionManagement().and()
- .securityContext().and()
- .requestCache().and()
- .anonymous().and()
- .servletApi().and()
- .apply(new DefaultLoginPageConfigurer<>()).and()
- .logout();
- // @formatter:on
- ClassLoader classLoader = this.context.getClassLoader();
- //spi 加载 AbstractHttpConfigurer的实现类,加入HttpSecurity中
- List<AbstractHttpConfigurer> defaultHttpConfigurers =
- SpringFactoriesLoader.loadFactories(AbstractHttpConfigurer.class, classLoader);
- for (AbstractHttpConfigurer configurer : defaultHttpConfigurers) {
- http.apply(configurer);
- }
- }
- //配置HttpSecurity,可自定义实现
- configure(http);
- return http;
- }
- protected AuthenticationManager authenticationManager() throws Exception {
- //如果没有初始化过,执行方法
- if (!authenticationManagerInitialized) {
-
- //configure(AuthenticationManagerBuilder auth)
- //交给子类自定义
- configure(localConfigureAuthenticationBldr);
-
- //如果没有自定义过
- if (disableLocalConfigureAuthenticationBldr) {
- //使用默认的
- authenticationManager = authenticationConfiguration
- .getAuthenticationManager();
- }
- else {
- //否则使用自定义过的进行构建
- authenticationManager = localConfigureAuthenticationBldr.build();
- }
- authenticationManagerInitialized = true;
- }
- return authenticationManager;
- }
以上代码可以看出,这个parent如果不自定义的话,会使用默认的authenticationConfiguration,而这个如下是spring注入进来的
- @Autowired
- public void setAuthenticationConfiguration(
- AuthenticationConfiguration authenticationConfiguration) {
- this.authenticationConfiguration = authenticationConfiguration;
- }
接下来再看AuthenticationConfiguration 是怎么加到spring容器中的。
还是@EnableWebSecurity注解,注解了一个@EnableGlobalAuthentication,这个注解import了一个AuthenticationConfiguration。
- //默认的情况获取AuthenticationManager
- public AuthenticationManager getAuthenticationManager() throws Exception {
- if (this.authenticationManagerInitialized) {
- return this.authenticationManager;
- }
- AuthenticationManagerBuilder authBuilder = this.applicationContext.getBean(AuthenticationManagerBuilder.class);
- if (this.buildingAuthenticationManager.getAndSet(true)) {
- return new AuthenticationManagerDelegator(authBuilder);
- }
- //放入三个configure,都在当前类中生成,GlobalAuthenticationConfigurerAdapter
- // InitializeUserDetailsBeanManagerConfigurer、InitializeAuthenticationProviderBeanManagerConfigurer
- for (GlobalAuthenticationConfigurerAdapter config : globalAuthConfigurers) {
- authBuilder.apply(config);
- }
-
- authenticationManager = authBuilder.build();
-
- if (authenticationManager == null) {
- authenticationManager = getAuthenticationManagerBean();
- }
-
- this.authenticationManagerInitialized = true;
- return authenticationManager;
- }
而在这个AuthenticationConfiguration中,会生成如上的AuthenticationManager,这个就是parent。
而在这个AuthenticationManager种,设置了多个globalAuthConfigurers
- @Bean
- public static GlobalAuthenticationConfigurerAdapter enableGlobalAuthenticationAutowiredConfigurer(
- ApplicationContext context) {
- return new EnableGlobalAuthenticationAutowiredConfigurer(context);
- }
-
- @Bean
- public static InitializeUserDetailsBeanManagerConfigurer initializeUserDetailsBeanManagerConfigurer(ApplicationContext context) {
- return new InitializeUserDetailsBeanManagerConfigurer(context);
- }
-
- @Bean
- public static InitializeAuthenticationProviderBeanManagerConfigurer initializeAuthenticationProviderBeanManagerConfigurer(ApplicationContext context) {
- return new InitializeAuthenticationProviderBeanManagerConfigurer(context);
- }
可以看到,当前类中有这么三个Configurer,其中InitializeUserDetailsBeanManagerConfigurer中会生成DaoAuthenticationProvider,要注意的是,这里并不是在这个类中直接生成的,而是如下,先注入了一个InitializeUserDetailsManagerConfigurer,在InitializeUserDetailsManagerConfigurer中创建的。
Configurer的逻辑前几篇博客已经说明过了
- @Override
- public void init(AuthenticationManagerBuilder auth) throws Exception {
- auth.apply(new InitializeUserDetailsManagerConfigurer());
- }
- public void configure(AuthenticationManagerBuilder auth) throws Exception {
- if (auth.isConfigured()) {
- return;
- }
- //从spring中获取UserDetailsService,如果没有,直接返回
- //所以要执行下面的代码,必须要有UserDetailsService
- UserDetailsService userDetailsService = getBeanOrNull(
- UserDetailsService.class);
- if (userDetailsService == null) {
- return;
- }
- //从spring中获取PasswordEncoder
- PasswordEncoder passwordEncoder = getBeanOrNull(PasswordEncoder.class);
-
- UserDetailsPasswordService passwordManager = getBeanOrNull(UserDetailsPasswordService.class);
-
- //构建DaoAuthenticationProvider
- DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
- provider.setUserDetailsService(userDetailsService);
- if (passwordEncoder != null) {
- provider.setPasswordEncoder(passwordEncoder);
- }
- if (passwordManager != null) {
- provider.setUserDetailsPasswordService(passwordManager);
- }
- provider.afterPropertiesSet();
-
- //将provider放入AuthenticationManagerBuilder中
- auth.authenticationProvider(provider);
- }
到此,DaoAuthenticationProvider的由来就已经清晰了。
再看看逻辑,如下代码,逻辑和之前描述一致。
- protected final UserDetails retrieveUser(String username,
- UsernamePasswordAuthenticationToken authentication)
- throws AuthenticationException {
- prepareTimingAttackProtection();
- try {
-
- //调用UserDetailsService的loadUserByUsername方法,方法需要自定义
- UserDetails loadedUser = this.getUserDetailsService().loadUserByUsername(username);
- if (loadedUser == null) {
- throw new InternalAuthenticationServiceException(
- "UserDetailsService returned null, which is an interface contract violation");
- }
- return loadedUser;
- }
- catch (UsernameNotFoundException ex) {
- mitigateAgainstTimingAttack(authentication);
- throw ex;
- }
- catch (InternalAuthenticationServiceException ex) {
- throw ex;
- }
- catch (Exception ex) {
- throw new InternalAuthenticationServiceException(ex.getMessage(), ex);
- }
- }
结束!
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。