注:分析的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,这个接口定义的是对用户信息管理的接口。