一、AuthenticationManager
AuthenticationManager是spring security中的认证管理器用来对登录请求进行处理。举个例子讲,在使用表单登录时处理用户的登录请求的是UsernamePasswordAuthenticationFilter
这个过滤器,它内部持有一个AuthenticationManager的对象,处理认证登录请求时会调用AuthenticationManager#authenticate方法处理请求。
1.1 AuthenticationManager
AuthenticationManager是一个接口,是对认证管理器进行的抽象,先来看下它的源码
public interface AuthenticationManager {
//调用authenticate对认证请求进行处理。
//入参是一个Authentication对象,封装了用户名和密码
//返回一个已经被认证过的Authentication对象
Authentication authenticate(Authentication authentication)
throws AuthenticationException;
}
1.2 Authentication
接着看下Authentication这个类,它用来对用户信息进行封装
public interface Authentication extends Principal, Serializable {
//这个方法返回用户的权限信息
Collection<? extends GrantedAuthority> getAuthorities();
//返回用户的凭证信息,例如密码
Object getCredentials();
/** 返回用户的一些额外信息,例如ip等,可以自定义多个属性
* Stores additional details about the authentication request. These might be an IP
* address, certificate serial number etc.
*
* @return additional details about the authentication request, or <code>null</code>
* if not used
*/
Object getDetails();
/** 返回用户主体,使用表单登录时返回的是用户的用户名
*/
Object getPrincipal();
//用来判断当前这个Authentication对象是否认证通过
boolean isAuthenticated();
//设置当前这个Authentication对象的认证状态
void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException;
}
接着看下UsernamePasswordAuthenticationFilter这个类中是如何使用认证管理器的
1.3 UsernamePasswordAuthenticationFilter
看一个过滤器的源码我们都会从doFilter方法看起,
UsernamePasswordAuthenticationFilter,这个类继承了AbstractAuthenticationProcessingFilter,doFilter方法定义在它的父类中,
AbstractAuthenticationProcessingFilter部分源码
public abstract class AbstractAuthenticationProcessingFilter extends GenericFilterBean
implements ApplicationEventPublisherAware, MessageSourceAware {
//该过滤器中持有的认证管理器对象
private AuthenticationManager authenticationManager;
//session管理器策略
private SessionAuthenticationStrategy sessionStrategy = new NullAuthenticatedSessionStrategy();
//登录成功和登录失败时的处理器
private AuthenticationSuccessHandler successHandler = new SavedRequestAwareAuthenticationSuccessHandler();
private AuthenticationFailureHandler failureHandler = new SimpleUrlAuthenticationFailureHandler();
//处理登录请求的方法
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
if (!requiresAuthentication(request, response)) {
chain.doFilter(request, response);
return;
}
if (logger.isDebugEnabled()) {
logger.debug("Request is to process authentication");
}
Authentication authResult;
try {
//处理登录请求的方法,这个方法在本类中是抽象方法,会被子类
//UsernamePasswordAuthenticationFilter实现
authResult = attemptAuthentication(request, response);
if (authResult == null) {
//说明UsernamePasswordAuthenticationFilter这个过滤器并没有完成认证但也没报错
// return immediately as subclass has indicated that it hasn't completed
// authentication
return;
}
sessionStrategy.onAuthentication(authResult, request, response);
}
catch (InternalAuthenticationServiceException failed) {
logger.error(
"An internal error occurred while trying to authenticate the user.",
failed);
//认证过程中报错了,属于认证失败。
unsuccessfulAuthentication(request, response, failed);
return;
}
catch (AuthenticationException failed) {
// Authentication failed
//认证过程中报错了,属于认证失败。
unsuccessfulAuthentication(request, response, failed);
return;
}
// Authentication success
if (continueChainBeforeSuccessfulAuthentication) {
//认证成功,继续执行过滤器链中其他的过滤器
chain.doFilter(request, response);
}
//认证成功的后续处理
successfulAuthentication(request, response, chain, authResult);
}
}
所以认证的关键就在子类的attemptAuthentication方法中
public class UsernamePasswordAuthenticationFilter extends
AbstractAuthenticationProcessingFilter {
public static final String SPRING_SECURITY_FORM_USERNAME_KEY = "username";
public static final String SPRING_SECURITY_FORM_PASSWORD_KEY = "password";
//这两个属性定义了从请求中获取用户名/密码时使用的参数名
private String usernameParameter = SPRING_SECURITY_FORM_USERNAME_KEY;
private String passwordParameter = SPRING_SECURITY_FORM_PASSWORD_KEY;
public Authentication attemptAuthentication(HttpServletRequest request,
HttpServletResponse response) throws AuthenticationException {
if (postOnly && !request.getMethod().equals("POST")) {
throw new AuthenticationServiceException(
"Authentication method not supported: " + request.getMethod());
}
//从请求对象中获取用户名和密码
String username = obtainUsername(request);
String password = obtainPassword(request);
if (username == null) {
username = "";
}
if (password == null) {
password = "";
}
username = username.trim();
//把用户名和密码封装成一个Authentication对象
UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(
username, password);
// Allow subclasses to set the "details" property
//在此过滤器中是空方法
setDetails(request, authRequest);
//先获取认证管理器,然后执行authenticate方法对authRequest这个Authentication对象进行认证
return this.getAuthenticationManager().authenticate(authRequest);
}
}
接下来就可以去看下认证管理器的认证逻辑
1.4 ProviderManager
AuthenticationManager
是一个接口,springsecurity提供了一个实现类ProviderManager
,来看下它的源码
ProviderManager部分源码:
public class ProviderManager implements AuthenticationManager, MessageSourceAware,
InitializingBean {
//重要属性,存储该认证管理器中持有的AuthenticationProvider
//认证时会循环调用持有的provider对请求进行处理,所以真正对请求进行认证的是各种provider
private List<AuthenticationProvider> providers = Collections.emptyList();
//当前认证管理器的父管理器,通常被称作全局认证管理器,
//如果循环完自己的providers后还不能对请求进行认证就会调用parent对请求进行处理
private AuthenticationManager parent;
//这个方法时具体的认证逻辑
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;
boolean debug = logger.isDebugEnabled();
//遍历该认证管理器中持有的providers
for (AuthenticationProvider provider : getProviders()) {
//看provider是否支持当前请求,如果不支持就跳过进行下个循环
if (!provider.supports(toTest)) {
continue;
}
if (debug) {
logger.debug("Authentication attempt using "
+ provider.getClass().getName());
}
try {
//此provider支持当前请求,调用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.
//如果自己的providers没有处理请求,再调用parent进行一次尝试
try {
result = parentResult = 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 = parentException = e;
}
}
if (result != null) {
//进到这里表示认证成功了
if (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 than it will publish an AuthenticationSuccessEvent
// This check prevents a duplicate AuthenticationSuccessEvent if the parent AuthenticationManager already published it
if (parentResult == null) {
eventPublisher.publishAuthenticationSuccess(result);
}
return result;
}
// Parent was null, or didn't authenticate (or throw an exception).
if (lastException == null) {
lastException = new ProviderNotFoundException(messages.getMessage(
"ProviderManager.providerNotFound",
new Object[] { toTest.getName() },
"No AuthenticationProvider found for {0}"));
}
// If the parent AuthenticationManager was attempted and failed than 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;
}
}
所以需要接着看下AuthenticationProvider
1.5 AuthenticationProvider
AuthenticationProvider也是一个接口
public interface AuthenticationProvider {
//具体的认证方法,只有支持时才会被调用
Authentication authenticate(Authentication authentication)
throws AuthenticationException;
//验证此provider是否支持当前请求
boolean supports(Class<?> authentication);
}
当我们使用表单登录时,使用的provider是DaoAuthenticationProvider
1.6 DaoAuthenticationProvider
DaoAuthenticationProvider#authenticate方法定义在它的父类AbstractUserDetailsAuthenticationProvider中,先看下这个方法
public abstract class AbstractUserDetailsAuthenticationProvider implements
AuthenticationProvider, InitializingBean, MessageSourceAware {
//具体的认证方法
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 {
//缓存中获取不到用户时在执行获取用户的方法
// 这个方法需要被子类实现,所以具体逻辑在DaoAuthenticationProvider
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(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();
}
//认证成功创建一个新的Authentication对象返回
return createSuccessAuthentication(principalToReturn, authentication, user);
}
protected Authentication createSuccessAuthentication(Object principal,
Authentication authentication, UserDetails user) {
UsernamePasswordAuthenticationToken result = new UsernamePasswordAuthenticationToken(
principal, authentication.getCredentials(),
authoritiesMapper.mapAuthorities(user.getAuthorities()));
result.setDetails(authentication.getDetails());
return result;
}
}
所以还需要在子类DaoAuthenticationProvider中看下retrieveUser,additionalAuthenticationChecks这两个方法
public class DaoAuthenticationProvider extends AbstractUserDetailsAuthenticationProvider {
//密码编码器
private PasswordEncoder passwordEncoder;
//查询用户信息的service
private UserDetailsService userDetailsService;
//查询用户
protected final UserDetails retrieveUser(String username,
UsernamePasswordAuthenticationToken authentication)
throws AuthenticationException {
prepareTimingAttackProtection();
try {
//调用自己的UserDetailsService
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);
}
}
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();
//把请求中传过来的密码密文和userDetails中查询出的密文进行匹配
if (!passwordEncoder.matches(presentedPassword, userDetails.getPassword())) {
logger.debug("Authentication failed: password does not match stored value");
throw new BadCredentialsException(messages.getMessage(
"AbstractUserDetailsAuthenticationProvider.badCredentials",
"Bad credentials"));
}
}
@Override
protected Authentication createSuccessAuthentication(Object principal,
Authentication authentication, UserDetails user) {
//这个是更新密码的逻辑,一般不会执行
boolean upgradeEncoding = this.userDetailsPasswordService != null
&& this.passwordEncoder.upgradeEncoding(user.getPassword());
if (upgradeEncoding) {
String presentedPassword = authentication.getCredentials().toString();
String newPassword = this.passwordEncoder.encode(presentedPassword);
user = this.userDetailsPasswordService.updatePassword(user, newPassword);
}
return super.createSuccessAuthentication(principal, authentication, user);
}
//对请求进行判断看是否支持
public boolean supports(Class<?> authentication) {
return (UsernamePasswordAuthenticationToken.class
.isAssignableFrom(authentication));
}
}
到这里还差查询用户的哪个UserDetailsService没有看
1.7 UserDetailsService
这个接口中定义了查询根据用户名查询用户的方法,会在DaoAuthenticationProvider中使用,需要我们提供他的实现类,通常在系统中会提供一个从数据库中查询用户的实现。
public interface UserDetailsService {
UserDetails loadUserByUsername(String username) throws UsernameNotFoundException;
}
到这里AuthenticationManager相关的功能就介绍完了,接下里我们研究下在springboot启动过程中这个对象是什么时候创建出来的。
二、AuthenticationManagerBuilder
AuthenticationManagerBuilder
是用来创建AuthenticationManager
的构建器,它实现了SecurityBuilder
,所以
SecurityBuilder
创建对象时的一些方法在它的继承体系中都有。
//部分源码
public class AuthenticationManagerBuilder
extends
AbstractConfiguredSecurityBuilder<AuthenticationManager, AuthenticationManagerBuilder>
implements ProviderManagerBuilder<AuthenticationManagerBuilder> {
//父级认证管理器
private AuthenticationManager parentAuthenticationManager;
//providers集合,这些Providers最后会被添加到构建出的AuthenticationManager中
private List<AuthenticationProvider> authenticationProviders = new ArrayList<>();
//这是一个用来给构建器中添加provider的方法
public AuthenticationManagerBuilder authenticationProvider(
AuthenticationProvider authenticationProvider) {
this.authenticationProviders.add(authenticationProvider);
return this;
}
//这个方法最终会被调用用来创建认证管理器对象
@Override
protected ProviderManager performBuild() throws Exception {
if (!isConfigured()) {
logger.debug("No authenticationProviders and no parentAuthenticationManager defined. Returning null.");
return null;
}
//创建认证管理器对象
ProviderManager providerManager = new ProviderManager(authenticationProviders,
parentAuthenticationManager);
if (eraseCredentials != null) {
providerManager.setEraseCredentialsAfterAuthentication(eraseCredentials);
}
if (eventPublisher != null) {
providerManager.setAuthenticationEventPublisher(eventPublisher);
}
//用处理器对providerManager进行处理,一般情况不会有这个步骤
providerManager = postProcess(providerManager);
return providerManager;
}
}
在spring security中创建认证管理器时都是使用这个构建器进行创建的,接下来就来研究下什么时候会创建
2.1 builder创建时机 WebSecurityConfigurerAdapter
我们对spring security的使用都是从自定义一个WebSecurityConfigurerAdapter的实现类开始的,更具体的内容可以看下这里spring seurity 自动配置的原理
WebSecurityConfigurerAdapter也是一个SecurityConfigurer,所以首先从这个类的init方法开始
public abstract class WebSecurityConfigurerAdapter implements
WebSecurityConfigurer<WebSecurity> {
//三个重要的属性
//用来生成全局认证管理器的配置类,可能用也可能不用
private AuthenticationConfiguration authenticationConfiguration;
//用来生成局部认证管理器的builder
private AuthenticationManagerBuilder authenticationBuilder;
//用来生成全局认证管理器的builder,其实就是上边builder生成的认证管理器的parent
private AuthenticationManagerBuilder localConfigureAuthenticationBldr;
//spring容器启动过程中会执行这个方法给上边的两个builder属性进行赋值
@Autowired
public void setApplicationContext(ApplicationContext context) {
this.context = context;
ObjectPostProcessor<Object> objectPostProcessor = context.getBean(ObjectPostProcessor.class);
LazyPasswordEncoder passwordEncoder = new LazyPasswordEncoder(context);
//这里创建了两个builder
//DefaultPasswordEncoderAuthenticationManagerBuilder是当前类的内部类
authenticationBuilder = new DefaultPasswordEncoderAuthenticationManagerBuilder(objectPostProcessor, passwordEncoder);
localConfigureAuthenticationBldr = new DefaultPasswordEncoderAuthenticationManagerBuilder(objectPostProcessor, passwordEncoder) {
@Override
public AuthenticationManagerBuilder eraseCredentials(boolean eraseCredentials) {
authenticationBuilder.eraseCredentials(eraseCredentials);
return super.eraseCredentials(eraseCredentials);
}
};
}
//容器启动过程中会给AuthenticationConfiguration这个属性赋值
@Autowired
public void setAuthenticationConfiguration(
AuthenticationConfiguration authenticationConfiguration) {
this.authenticationConfiguration = authenticationConfiguration;
}
//重点的init方法,这个方法的分析可以看下上边连接给出的另一篇博客
public void init(final WebSecurity web) throws Exception {
//获取http对象
final HttpSecurity http = getHttp();
web.addSecurityFilterChainBuilder(http).postBuildAction(new Runnable() {
public void run() {
FilterSecurityInterceptor securityInterceptor = http
.getSharedObject(FilterSecurityInterceptor.class);
web.securityInterceptor(securityInterceptor);
}
});
}
//getHttp方法
protected final HttpSecurity getHttp() throws Exception {
if (http != null) {
return http;
}
DefaultAuthenticationEventPublisher eventPublisher = objectPostProcessor
.postProcess(new DefaultAuthenticationEventPublisher());
localConfigureAuthenticationBldr.authenticationEventPublisher(eventPublisher);
//获取全局的认证管理器,也就是parent,这个方法重点看下
AuthenticationManager authenticationManager = authenticationManager();
//设置parent,
//认证的时候如果自己不能认证就会使用parent再进行一次认证
//这个builder创建的是局部的认证管理器,它会被放到sharedObjects中,
//在HttpSecurity的beforeConfigure方法中会拿出这个buildr执行得到一个
//局部的AuthenticationManager在放进sharedObjects中,这部分内容可以看下我的另一篇
//分析FormLoginConfigurer的博客
authenticationBuilder.parentAuthenticationManager(authenticationManager);
authenticationBuilder.authenticationEventPublisher(eventPublisher);
Map<Class<? extends Object>, Object> sharedObjects = createSharedObjects();
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();
ClassLoader classLoader = this.context.getClassLoader();
List<AbstractHttpConfigurer> defaultHttpConfigurers =
SpringFactoriesLoader.loadFactories(AbstractHttpConfigurer.class, classLoader);
for (AbstractHttpConfigurer configurer : defaultHttpConfigurers) {
http.apply(configurer);
}
}
configure(http);
return http;
}
//这个方法用来获取全局的认证管理器
protected AuthenticationManager authenticationManager() throws Exception {
if (!authenticationManagerInitialized) {
//这个configure方法可以被子类重写,对localConfigureAuthenticationBldr进行一些定制
//如果没重写这个方法将不会使用localConfigureAuthenticationBldr来构建
//AuthenticationManager,使用springsecurity的自动配置
configure(localConfigureAuthenticationBldr);
if (disableLocalConfigureAuthenticationBldr) {
//进到这里表示使用authenticationConfiguration中的自动配置来生成全局认证管理器
//调用getAuthenticationManager方法
authenticationManager = authenticationConfiguration
.getAuthenticationManager();
}
else {
//进到这里表示重写configure方法自定义了localConfigureAuthenticationBldr,
//就使用它来创建,不使用authenticationConfiguration中的自动配置
authenticationManager = localConfigureAuthenticationBldr.build();
}
authenticationManagerInitialized = true;
}
return authenticationManager;
}
//上边configure方法本类的实现
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
//禁用全局认证管理器的builder,意思是不使用这个builder,使用springsecurity的自动配置
this.disableLocalConfigureAuthenticationBldr = true;
}
}
2.2 AuthenticationConfiguration中的自动配置
AuthenticationConfiguration针对AuthenticationManager做了许多自动配置,保证能够生成一个AuthenticationManager对象,我们先来研究下使用自动配置时会如何创建出一个AuthenticationManager对象。
在WebSecurityConfigurerAdapter中是通过调用authenticationConfiguration.getAuthenticationManager方法来生成认证管理器对象。
@Configuration
@Import(ObjectPostProcessorConfiguration.class)
public class AuthenticationConfiguration {
// 存储特定SecurityConfigurer的集合,下边会有一个set方法自动给此属性注入值
private List<GlobalAuthenticationConfigurerAdapter> globalAuthConfigurers = Collections
.emptyList();
//这个类中给spring容器中注入了几个bean
//注入了一个认证管理器的构建器
@Bean
public AuthenticationManagerBuilder authenticationManagerBuilder(
ObjectPostProcessor<Object> objectPostProcessor, ApplicationContext context) {
LazyPasswordEncoder defaultPasswordEncoder = new LazyPasswordEncoder(context);
AuthenticationEventPublisher authenticationEventPublisher = getBeanOrNull(context, AuthenticationEventPublisher.class);
DefaultPasswordEncoderAuthenticationManagerBuilder result = new DefaultPasswordEncoderAuthenticationManagerBuilder(objectPostProcessor, defaultPasswordEncoder);
if (authenticationEventPublisher != null) {
result.authenticationEventPublisher(authenticationEventPublisher);
}
return result;
}
@Bean
public static GlobalAuthenticationConfigurerAdapter enableGlobalAuthenticationAutowiredConfigurer(
ApplicationContext context) {
return new EnableGlobalAuthenticationAutowiredConfigurer(context);
}
//给容器中注入了一个SecurityConfigurer,
//这个configurer会给authenticationManagerBuilder中注入一个DaoAuthenticationProvider,
//下边会具体分析
@Bean
public static InitializeUserDetailsBeanManagerConfigurer initializeUserDetailsBeanManagerConfigurer(ApplicationContext context) {
return new InitializeUserDetailsBeanManagerConfigurer(context);
}
//给容器中注入了一个SecurityConfigurer
//这个configurer会从容器中获取AuthenticationProvider并添加到authenticationManagerBuilder中
@Bean
public static InitializeAuthenticationProviderBeanManagerConfigurer initializeAuthenticationProviderBeanManagerConfigurer(ApplicationContext context) {
return new InitializeAuthenticationProviderBeanManagerConfigurer(context);
}
//这个方法会给当前类自动注入SecurityConfigurer,实际上会把上边用@Bean放入容器的三个
//SecurityConfigurer注入进来。
@Autowired(required = false)
public void setGlobalAuthenticationConfigurers(
List<GlobalAuthenticationConfigurerAdapter> configurers) throws Exception {
Collections.sort(configurers, AnnotationAwareOrderComparator.INSTANCE);
this.globalAuthConfigurers = configurers;
}
//这个方法就是用来获取AuthenticationManager的
public AuthenticationManager getAuthenticationManager() throws Exception {
if (this.authenticationManagerInitialized) {
return this.authenticationManager;
}
//先拿到一个AuthenticationManagerBuilder,调的是本类的方法
AuthenticationManagerBuilder authBuilder = authenticationManagerBuilder(
this.objectPostProcessor, this.applicationContext);
if (this.buildingAuthenticationManager.getAndSet(true)) {
return new AuthenticationManagerDelegator(authBuilder);
}
//把属性globalAuthConfigurers中所有的configurer应用到authBuilder中,
//这个属性值是spring自动注入进来的,实际就是上边用@Bean放进来的三个configurer
for (GlobalAuthenticationConfigurerAdapter config : globalAuthConfigurers) {
authBuilder.apply(config);
}
//执行构建器的build方法
authenticationManager = authBuilder.build();
if (authenticationManager == null) {
authenticationManager = getAuthenticationManagerBean();
}
this.authenticationManagerInitialized = true;
return authenticationManager;
}
}
接下里需要看下上边用@Bean放进来的几个Configurer的作用
2.3.1 InitializeUserDetailsBeanManagerConfigurer
class InitializeUserDetailsBeanManagerConfigurer
extends GlobalAuthenticationConfigurerAdapter {
static final int DEFAULT_ORDER = Ordered.LOWEST_PRECEDENCE - 5000;
private final ApplicationContext context;
/**
* @param context
*/
public InitializeUserDetailsBeanManagerConfigurer(ApplicationContext context) {
this.context = context;
}
@Override
public void init(AuthenticationManagerBuilder auth) throws Exception {
//这个configurer的init方法又给builder中添加了一个内部类
auth.apply(new InitializeUserDetailsManagerConfigurer());
}
class InitializeUserDetailsManagerConfigurer
extends GlobalAuthenticationConfigurerAdapter {
@Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
if (auth.isConfigured()) {
return;
}
//从容器中获取UserDetailsService的实现类
UserDetailsService userDetailsService = getBeanOrNull(
UserDetailsService.class);
if (userDetailsService == null) {
return;
}
//从容器中获取PasswordEncoder的实现类
PasswordEncoder passwordEncoder = getBeanOrNull(PasswordEncoder.class);
UserDetailsPasswordService passwordManager = getBeanOrNull(UserDetailsPasswordService.class);
//创建DaoAuthenticationProvider
DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
//给provider中的UserDetailsService赋值
provider.setUserDetailsService(userDetailsService);
if (passwordEncoder != null) {
provider.setPasswordEncoder(passwordEncoder);
}
if (passwordManager != null) {
provider.setUserDetailsPasswordService(passwordManager);
}
provider.afterPropertiesSet();
//把provider添加到构建器中
auth.authenticationProvider(provider);
}
}
所以说这个configurer给AuthenticationManagerBuilder中添加了一个DaoAuthenticationProvider。
上边提到了从容器中获取UserDetailsService的实现类,如果有自定义当然会获取到自定义的那个,那如果没有自定义会怎么样呢,需要看下UserDetailsServiceAutoConfiguration
2.3.2 UserDetailsServiceAutoConfiguration
@Configuration
@ConditionalOnClass(AuthenticationManager.class)
@ConditionalOnBean(ObjectPostProcessor.class)
//条件注解,这个自动配置类只有在使用者没有自定义AuthenticationProvider,UserDetailsService时
//才会生效。
@ConditionalOnMissingBean({ AuthenticationManager.class, AuthenticationProvider.class,
UserDetailsService.class })
public class UserDetailsServiceAutoConfiguration {
private static final String NOOP_PASSWORD_PREFIX = "{noop}";
private static final Pattern PASSWORD_ALGORITHM_PATTERN = Pattern
.compile("^\\{.+}.*$");
private static final Log logger = LogFactory
.getLog(UserDetailsServiceAutoConfiguration.class);
//这个方法给容器中注入了一个基于内存的UserDetails实现,
@Bean
@ConditionalOnMissingBean(type = "org.springframework.security.oauth2.client.registration.ClientRegistrationRepository")
@Lazy
public InMemoryUserDetailsManager inMemoryUserDetailsManager(
SecurityProperties properties,
ObjectProvider<PasswordEncoder> passwordEncoder) {
SecurityProperties.User user = properties.getUser();
List<String> roles = user.getRoles();
return new InMemoryUserDetailsManager(User.withUsername(user.getName())
.password(getOrDeducePassword(user, passwordEncoder.getIfAvailable()))
.roles(StringUtils.toStringArray(roles)).build());
}
private String getOrDeducePassword(SecurityProperties.User user,
PasswordEncoder encoder) {
String password = user.getPassword();
if (user.isPasswordGenerated()) {
logger.info(String.format("%n%nUsing generated security password: %s%n",
user.getPassword()));
}
if (encoder != null || PASSWORD_ALGORITHM_PATTERN.matcher(password).matches()) {
return password;
}
return NOOP_PASSWORD_PREFIX + password;
}
}
2.3.3 InitializeAuthenticationProviderBeanManagerConfigurer
再看下InitializeAuthenticationProviderBeanManagerConfigurer
class InitializeAuthenticationProviderBeanManagerConfigurer
extends GlobalAuthenticationConfigurerAdapter {
static final int DEFAULT_ORDER = InitializeUserDetailsBeanManagerConfigurer.DEFAULT_ORDER
- 100;
private final ApplicationContext context;
/**
* @param context the ApplicationContext to look up beans.
*/
public InitializeAuthenticationProviderBeanManagerConfigurer(
ApplicationContext context) {
this.context = context;
}
@Override
public void init(AuthenticationManagerBuilder auth) throws Exception {
//同样的套路
auth.apply(new InitializeUserDetailsManagerConfigurer());
}
class InitializeUserDetailsManagerConfigurer
extends GlobalAuthenticationConfigurerAdapter {
@Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
if (auth.isConfigured()) {
return;
}
//从容器中获取AuthenticationProvider
AuthenticationProvider authenticationProvider = getBeanOrNull(
AuthenticationProvider.class);
if (authenticationProvider == null) {
return;
}
//添加到builder中
auth.authenticationProvider(authenticationProvider);
}
//从这个方法可以看出如果容器中存在多个type类型的bean会返回null
private <T> T getBeanOrNull(Class<T> type) {
String[] userDetailsBeanNames = InitializeAuthenticationProviderBeanManagerConfigurer.this.context
.getBeanNamesForType(type);
if (userDetailsBeanNames.length != 1) {
return null;
}
return InitializeAuthenticationProviderBeanManagerConfigurer.this.context
.getBean(userDetailsBeanNames[0], type);
}
}
}
2.3 localConfigureAuthenticationBldr的使用
上边第2.2节讲的使用spring security提供的自动配置来生成认证管理器,当不想使用自动配置时就要重写
WebSecurityConfigurerAdapter#configure(org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder)
这个方法,2.1节有提到
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
//重写时要删掉这句,表示启用 LocalConfigureAuthenticationBldr
this.disableLocalConfigureAuthenticationBldr = true;
//调用auth提供的方法对它进行定制,一般最常用的是添加自己的AuthenticationProvider,
//这样后边创建出的认证管理器就会使用我们自己的AuthenticationProvider对请求进行认证
auth.authenticationProvider(myBuilder)
}