UsernamePasswordAuthenticationFilter->doFilter
->AbstractAuthenticationProcessingFilter#doFilter
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
throws IOException, ServletException {
...
Authentication authResult;
try {
//step1:获得Authentication对象,这是个抽象方法,具体实现在子类中
authResult = attemptAuthentication(request, response);
if (authResult == null) {
// return immediately as subclass has indicated that it hasn't completed
// authentication
return;
}
sessionStrategy.onAuthentication(authResult, request, response);
}
//step2:如果获得Authentication 对象失败,对失败的Authentication 进行处理
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;
}
// step3: 如果成功,继续处理下一个filter,
if (continueChainBeforeSuccessfulAuthentication) {
chain.doFilter(request, response);
}
//step4:处理成功的处理
successfulAuthentication(request, response, chain, authResult);
}
如何生成Authentication 对象?
即上面中第一步的具体方法:
attemptAuthentication(request, response)
->UsernamePasswordAuthenticationFilter#attemptAuthentication
public Authentication attemptAuthentication(HttpServletRequest request,
HttpServletResponse response) throws AuthenticationException {
//只支持post请求
//获取用户名密码
//使用用户名密码构建一个Token对象
UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(
username, password);
// Allow subclasses to set the "details" property
setDetails(request, authRequest);
//核心方法,调用AuthenticationManager().authenticate来完成验证,并返回Authentication对象
return this.getAuthenticationManager().authenticate(authRequest);
}
->this.getAuthenticationManager().authenticate(authRequest)
->ProviderManager#authenticate
-> parent.authenticate(authentication)
->ProviderManager#authenticate
->AbstractUserDetailsAuthenticationProvider#authenticate.authenticate(authentication)
user = retrieveUser(username,
(UsernamePasswordAuthenticationToken) authentication);
->DaoAuthenticationProvider#retrieveUser
protected final UserDetails retrieveUser(String username,
UsernamePasswordAuthenticationToken authentication)
throws AuthenticationException {
UserDetails loadedUser;
try {
loadedUser = this.getUserDetailsService().loadUserByUsername(username);
->DemoUserDetailsService#loadUserByUsername
in this method , return the userdetail object
问题:哪里调用setUserDetailsService?
上面一篇文章说的是fiter流程,其中最关键的是AbstractConfiguredSecurityBuilder#doBuild
这里有很多构建过程,其中一步是 BrowserSecurityConfig,即我们自定义的配置类初始化的过程,
org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter#init
->org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter#getHttp
->AuthenticationManager authenticationManager = authenticationManager();这里需要先构建AuthenticationManager对象
构建的过程也是调用AbstractConfiguredSecurityBuilder#doBuild 这里面的模板方法,不过初始化调用的是
org.springframework.boot.autoconfigure.security.AuthenticationManagerConfiguration.SpringBootAuthenticationConfigurerAdapter#init
==》这里会构建一个默认的对象
auth.apply(new AuthenticationManagerConfiguration.DefaultInMemoryUserDetailsManagerConfigurer(this.securityProperties))
==》之后调用 模板方法里面的
AbstractConfiguredSecurityBuilder#configure
InitializeUserDetailsBeanManagerConfigurer.InitializeUserDetailsManagerConfigurer#configure
UserDetailsService 以及加密方式PasswordEncoder 都在这里设置进来
public void configure(AuthenticationManagerBuilder auth) throws Exception {
if (auth.isConfigured()) {
return;
}
UserDetailsService userDetailsService = getBeanOrNull(
UserDetailsService.class);
if (userDetailsService == null) {
return;
}
PasswordEncoder passwordEncoder = getBeanOrNull(PasswordEncoder.class);
DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
provider.setUserDetailsService(userDetailsService);
if (passwordEncoder != null) {
provider.setPasswordEncoder(passwordEncoder);
}
auth.authenticationProvider(provider);
}