注:分析的Springsecurity版本是4.3.x,源碼可自行到github上去下載。java
先上一張圖,以下圖1.1所示:git
圖1.1 Springsecurity認證的時序圖github
AuthenticationManager的實現類ProviderManager的實現以下:spring
List-1.1 ProviderManager的authenticate方法ide
public class ProviderManager implements AuthenticationManager, MessageSourceAware, InitializingBean { private List<AuthenticationProvider> providers = Collections.emptyList(); private AuthenticationManager parent; public Authentication authenticate(Authentication authentication) throws AuthenticationException { Class<? extends Authentication> toTest = authentication.getClass(); AuthenticationException lastException = null; Authentication result = null; boolean debug = logger.isDebugEnabled(); for (AuthenticationProvider provider : getProviders()) { //調用supports方法 if (!provider.supports(toTest)) { continue; } if (debug) { logger.debug("Authentication attempt using " + provider.getClass().getName()); } try { //調用provider的authenticate方法 result = provider.authenticate(authentication); if (result != null) { copyDetails(authentication, result); break; } } catch (AccountStatusException e) { prepareException(e, authentication); // SEC-546: Avoid polling additional providers if auth failure is due to // invalid account status throw e; } catch (InternalAuthenticationServiceException e) { prepareException(e, authentication); throw e; } catch (AuthenticationException e) { lastException = e; } } if (result == null && parent != null) { // Allow the parent to try. try { //若是result爲null且parent不爲null,那麼調用parent的authenticate方法 result = parent.authenticate(authentication); } catch (ProviderNotFoundException e) { // 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 e) { lastException = e; } } } ...
AuthenticationProvider是個接口,以下List-2.1所示:post
List-2.1 AuthenticationProvider的接口定義this
/** * Indicates a class can process a specific * {@link org.springframework.security.core.Authentication} implementation. * * @author Ben Alex */ public interface AuthenticationProvider { Authentication authenticate(Authentication authentication) throws AuthenticationException; boolean supports(Class<?> authentication); }
以DaoAuthenticationProvider爲例,它的繼承圖以下所示:spa
圖2.1 DaoAuthenticationProvider的繼承圖debug
AbstractUserDetailsAuthenticationProvider中的authenticate方法中,定義了主要的模板,即它裏面使用了Template方法。以下List-2.2所示:code
List-2.2 AbstractUserDetailsAuthenticationProvider的authenticate方法
public Authentication authenticate(Authentication authentication) throws AuthenticationException { Assert.isInstanceOf(UsernamePasswordAuthenticationToken.class, authentication, messages.getMessage( "AbstractUserDetailsAuthenticationProvider.onlySupports", "Only UsernamePasswordAuthenticationToken is supported")); // Determine username String username = (authentication.getPrincipal() == null) ? "NONE_PROVIDED" : authentication.getName(); boolean cacheWasUsed = true; UserDetails user = this.userCache.getUserFromCache(username); if (user == null) { cacheWasUsed = false; try { //retrieveUser方法是抽象的,它由子類來實現 user = retrieveUser(username, (UsernamePasswordAuthenticationToken) authentication); } catch (UsernameNotFoundException notFound) { logger.debug("User '" + username + "' not found"); if (hideUserNotFoundExceptions) { throw new BadCredentialsException(messages.getMessage( "AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials")); } else { throw notFound; } } Assert.notNull(user, "retrieveUser returned null - a violation of the interface contract"); } try { preAuthenticationChecks.check(user); //additionalAuthenticationChecks方法是抽象的,由子類來實現。 additionalAuthenticationChecks(user, (UsernamePasswordAuthenticationToken) authentication); } catch (AuthenticationException exception) { if (cacheWasUsed) { // There was a problem, so try again after checking // we're using latest data (i.e. not from the cache) cacheWasUsed = false; user = retrieveUser(username, (UsernamePasswordAuthenticationToken) authentication); preAuthenticationChecks.check(user); additionalAuthenticationChecks(user, (UsernamePasswordAuthenticationToken) authentication); } else { throw exception; } } postAuthenticationChecks.check(user); if (!cacheWasUsed) { this.userCache.putUserInCache(user); } Object principalToReturn = user; if (forcePrincipalAsString) { principalToReturn = user.getUsername(); } return createSuccessAuthentication(principalToReturn, authentication, user); }
retrieveUser和additionalAuthenticationChecks由子類DaoAuthenticationProvider來實現。DaoAuthenticationProvider中的retrieveUser方法,涉及UserDetailsService。
它的實現以下List-3.1所示:
List-3.1 UserDetailsService定義
public interface UserDetailsService { UserDetails loadUserByUsername(String username) throws UsernameNotFoundException; }
這個接口的有個實現類JdbcUserDetailsManager,它實現了接口UserDetailsManager,這個接口定義的是對用戶信息管理的接口。