之后会被/login页面拦截,在/login页面拦截之后输入用户密码,会被formLogin()方法进行捕获(根据类SpringBootWebSecurityConfiguration显示)
查看该方法,发现该方法应用了FormLoginConfigurer的实现,进入该类
该类中调用了父类的UsernamePasswordAuthenticationFilter,因此肯定会被此Filter捕获到,会进入到该类中。
因此在该类中attempAuthentication方法处打断点,发送请求,点击登录之后,会进入到此处,可以发现最后返回的是authencicate方法,并且将验证请求信息传了进去,进入该方法查看。
AuthenticationManager会调用providerManager,providerManager提供者会有很多种认证方式,如果返回一个完整的Authenticate,则代表认证成功。
根据源代码发现,authenticate会回调父类中的authenticate方法。由于父类中的AuthenticationManager是一个接口,因此接口的实现类只能靠子类,因此,他回调的时候是回调了他自己的authenticate的方法。
ProviderManager的父接口是AuthenticationManager
AuthenticationManager是一个接口。
再次进入到authentication方法,认证是通过providerManager不断的循环,直到能够找到能够认证通过的就认证成功,如果没有找到,那么就认证失败。
在这个回调之后,可以发现,终于有了自己的实现,进入该方法内。
进入该方法后,可以发现,这次并不是ProviderManager类中的方法了,而是AbstractUserDetailsAuthenticationProvider类中的方法,也就是说AbstractUserDetailsAuthenticationProvider类是ProviderManager类的实现类,他也实现了这个authenticate方法。该方法中调用了determineUsername方法,去判断当前身份是否为空,如果为空,则返回NONE_PROVIDED,如果不为空,则返回用户名。说明该方法并不是完成认证的方法。
determineUsername方法
retrieveUser是真正做认证的方法,因此进入该方法中进行查看。
retrieveUser中,可以看到该语句
UserDetails loadedUser = this.getUserDetailsService().loadUserByUsername(username);
说明他会拿着用户输入的用户名去UserDetailsService中的loadUserByUsername方法,因此只需要查看loadUserByUsername的具体实现即可。
查看 this.getUserDetailsService()的实现,可以发现他的结果是一个InMemoryUserDetailsManager,说明是基于内存的实现。
因此认证时所需要的数据源是Spring Security基于内存的实现。
UserDetailService
public interface UserDetailsService {
UserDetails loadUserByUsername(String username) throws UsernameNotFoundException;
}
UserDetailService是顶层父接口,接口中loadUserByUserName方法是用来在用户认证时进行用户名认证方法,默认实现使用内存实现,如果想要修改数据库实现,只需要自定义UserDetailService实现,最终返回UserDetails实例即可。
由UserDetailsManager的类可以发现,InMemoryUserDetailsManager类会对loadUserByUserName方法进行实现,查看该类
该方法对本类中的users进行获取,之后返回一个完整的users对象。
由于UserDetailsService默认是基于内存的配置,因此查看他的自动配置信息。
这个类的源码非常多,关键部分:
//该类生效条件
@Configuration(proxyBeanMethods = false)
//当类路径下存在AuthenticationManager.class时,默认引入Spring Security即会存在。
@ConditionalOnClass(AuthenticationManager.class)
//当类中存在ObjectPostProcessor.class时,该类为Spring容器中存在。
@ConditionalOnBean(ObjectPostProcessor.class)
//当前工程中没有自定义AuthenticationManager.class, AuthenticationProvider.class, UserDetailsService.class,
// AuthenticationManagerResolver.class这四个实例时。
@ConditionalOnMissingBean(
value = { AuthenticationManager.class, AuthenticationProvider.class, UserDetailsService.class,
AuthenticationManagerResolver.class },
type = { "org.springframework.security.oauth2.jwt.JwtDecoder",
"org.springframework.security.oauth2.server.resource.introspection.OpaqueTokenIntrospector",
"org.springframework.security.oauth2.client.registration.ClientRegistrationRepository" })
public class UserDetailsServiceAutoConfiguration {
// ...
@Bean
@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());
}
// ...
}
默认情况下该类中的条件都能够满足,此时Spring Security会提供一个InMemoryUserDetailManager实例。
说明User是SecurityProperties的静态内部类,查看getUser方法,可以发现返回一个User,查看User。
该静态内部类中初始化了一个username为:user。
password为UUID随机生成的。
发现该类中出现@ConfigurationProperties注解,给属性注入,因此以后可以在配置文件中修改默认的username,password以及roles。