前面三篇写的关于Spring Security是如何进行用户认证,本篇来看看我们输入的用户名和密码是如何传给AuthenticationManager的。
Spring Security有一个FilterChain,它包含由多个Filter组成的集合。当用户输入用户密码,并提交HttpRequest后,Spring Security通过一系列的Filter对HttpRequest进行处理和筛选。下面的图可以看出,Spring Security Filter Chain 作为Servlet Filter中的一个,但它包含了多个Filter。
在org.springframework.security.config.annotation.web.configurers包下有许多configurer类。这些configurer类,都实现了configurer方法。当Spring Security初始化时,会根据代码中定义的Configurer配置或者properties文件中的配置,得到一个Configurer集合。集合中的每个Configurer类中的configurer方法被调用时,就可往SecurityFilterChain中添加相应的Filter。
之前在Spring Security系列-Spring Security简单身份认证配置(二)中配置了的身份认证,它所使用的Configure以及生成的Filter如下:
FiltersConfigurersorg.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilterWebSecurityConfigurerAdapter直接添加org.springframework.security.web.context.SecurityContextPersistenceFilterSecurityContextConfigurerorg.springframework.security.web.header.HeaderWriterFilterHeadersConfigurerorg.springframework.security.web.csrf.CsrfFilterCsrfConfigurerorg.springframework.security.web.authentication.logout.LogoutFilterLogoutConfigurerorg.springframework.security.web.authentication.UsernamePasswordAuthenticationFilterFormLoginConfigurerorg.springframework.security.web.savedrequest.RequestCacheAwareFilterRequestCacheConfigurerorg.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilterServletApiConfigurerorg.springframework.security.web.authentication.AnonymousAuthenticationFilterAnonymousConfigurerorg.springframework.security.web.session.SessionManagementFilterCsrfCoSessionManagementConfigurerfigurerorg.springframework.security.web.access.ExceptionTranslationFilterExceptionHandlingConfigurerorg.springframework.security.web.access.intercept.FilterSecurityInterceptorExpressionUrlAuthorizationConfigurer下面是调用每个Configurer中的configure方法
public abstract class AbstractConfiguredSecurityBuilder<O, B extends SecurityBuilder<O>> extends AbstractSecurityBuilder<O> { private void configure() throws Exception { Collection<SecurityConfigurer<O, B>> configurers = getConfigurers(); for (SecurityConfigurer<O, B> configurer : configurers) { configurer.configure((B) this); } } }下面是类WebSecurityConfigurerAdapter中,加载默认的配置
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(); List<AbstractHttpConfigurer> defaultHttpConfigurers = SpringFactoriesLoader.loadFactories(AbstractHttpConfigurer.class, classLoader); for (AbstractHttpConfigurer configurer : defaultHttpConfigurers) { http.apply(configurer); } }从UsernamePasswordAuthenticationFilter类中的,attemptAuthentication方法可以看到使用了AuthenticationManager的authenticate方法来验证HttpRequest中的username和password。看过上篇的读者应该记得,上篇我们的通过控制台输入的用户密码,然后进行验证,也是通过这种方式运行的。
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(); UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken( username, password); // Allow subclasses to set the "details" property setDetails(request, authRequest); return this.getAuthenticationManager().authenticate(authRequest); }