咱们来继续撸 Spring Security 源码。java
SecurityConfigurer 在 Spring Security 中是一个很是重要的角色。在前面的文章中,松哥曾经屡次提到过,Spring Security 过滤器链中的每个过滤器,都是经过 xxxConfigurer 来进行配置的,而这些 xxxConfigurer 实际上都是 SecurityConfigurer 的实现。git
因此咱们今天有必要来跟你们把 SecurityConfigurer 从头至尾捋一捋。github
SecurityConfigurer 自己是一个接口,咱们来看下:数据库
public interface SecurityConfigurer<O, B extends SecurityBuilder<O>> {
void init(B builder) throws Exception;
void configure(B builder) throws Exception;
}
复制代码
能够看到,SecurityConfigurer 中主要是两个方法,init 和 configure。session
init 就是一个初始化方法。而 configure 则是一个配置方法。这里只是规范了方法的定义,具体的实现则在不一样的实现类中。app
须要注意的是这两个方法的参数类型都是一个泛型 B,也就是 SecurityBuilder 的子类,关于 SecurityBuilder ,它是用来构建过滤器链的,松哥将在下篇文章中和你们介绍。ide
SecurityConfigurer 有三个实现类:函数
咱们分别来看。oop
SecurityConfigurerAdapter 实现了 SecurityConfigurer 接口,咱们所使用的大部分的 xxxConfigurer 也都是 SecurityConfigurerAdapter 的子类。post
SecurityConfigurerAdapter 在 SecurityConfigurer 的基础上,还扩展出来了几个很是好用的方法,咱们一块儿来看下:
public abstract class SecurityConfigurerAdapter<O, B extends SecurityBuilder<O>> implements SecurityConfigurer<O, B> {
private B securityBuilder;
private CompositeObjectPostProcessor objectPostProcessor = new CompositeObjectPostProcessor();
public void init(B builder) throws Exception {
}
public void configure(B builder) throws Exception {
}
public B and() {
return getBuilder();
}
protected final B getBuilder() {
if (securityBuilder == null) {
throw new IllegalStateException("securityBuilder cannot be null");
}
return securityBuilder;
}
@SuppressWarnings("unchecked")
protected <T> T postProcess(T object) {
return (T) this.objectPostProcessor.postProcess(object);
}
public void addObjectPostProcessor(ObjectPostProcessor<?> objectPostProcessor) {
this.objectPostProcessor.addObjectPostProcessor(objectPostProcessor);
}
public void setBuilder(B builder) {
this.securityBuilder = builder;
}
private static final class CompositeObjectPostProcessor implements ObjectPostProcessor<Object> {
private List<ObjectPostProcessor<?>> postProcessors = new ArrayList<>();
@SuppressWarnings({ "rawtypes", "unchecked" })
public Object postProcess(Object object) {
for (ObjectPostProcessor opp : postProcessors) {
Class<?> oppClass = opp.getClass();
Class<?> oppType = GenericTypeResolver.resolveTypeArgument(oppClass,
ObjectPostProcessor.class);
if (oppType == null || oppType.isAssignableFrom(object.getClass())) {
object = opp.postProcess(object);
}
}
return object;
}
private boolean addObjectPostProcessor( ObjectPostProcessor<?> objectPostProcessor) {
boolean result = this.postProcessors.add(objectPostProcessor);
postProcessors.sort(AnnotationAwareOrderComparator.INSTANCE);
return result;
}
}
}
复制代码
这即是 SecurityConfigurerAdapter 的主要功能,后面大部分的 xxxConfigurer 都是基于此类来实现的。
GlobalAuthenticationConfigurerAdapter 看名字就知道是一个跟全局配置有关的东西,它自己实现了 SecurityConfigurerAdapter 接口,可是并未对方法作具体的实现,只是将泛型具体化了:
@Order(100)
public abstract class GlobalAuthenticationConfigurerAdapter implements SecurityConfigurer<AuthenticationManager, AuthenticationManagerBuilder> {
public void init(AuthenticationManagerBuilder auth) throws Exception {
}
public void configure(AuthenticationManagerBuilder auth) throws Exception {
}
}
复制代码
能够看到,SecurityConfigurer 中的泛型,如今明确成了 AuthenticationManager 和 AuthenticationManagerBuilder。因此 GlobalAuthenticationConfigurerAdapter 的实现类未来主要是和配置 AuthenticationManager 有关。固然也包括默认的用户名密码也是由它的实现类来进行配置的。
咱们在 Spring Security 中使用的 AuthenticationManager 其实能够分为两种,一种是局部的,另外一种是全局的,这里主要是全局的配置。
还有一个实现类就是 WebSecurityConfigurer,这个可能有的小伙伴比较陌生,其实他就是咱们每天用的 WebSecurityConfigurerAdapter 的父接口。
因此 WebSecurityConfigurer 的做用就很明确了,用户扩展用户自定义的配置。
SecurityConfigurer 默认主要是这三个实现,考虑到大多数的过滤器配置都是经过 SecurityConfigurerAdapter 进行扩展的,所以咱们今天就经过这条线进行展开。另外两条线松哥也将撸两篇文章和你们介绍。
SecurityConfigurerAdapter 的实现主要也是三大类:
考虑到 LDAP 如今使用不多,因此这里我来和你们重点介绍下前两个。
这个配置类看名字大概就知道这是用来配置用户类的。
AbstractDaoAuthenticationConfigurer
AbstractDaoAuthenticationConfigurer 中所作的事情比较简单,主要是构造了一个默认的 DaoAuthenticationProvider,并为其配置 PasswordEncoder 和 UserDetailsService。
UserDetailsServiceConfigurer
UserDetailsServiceConfigurer 重写了 AbstractDaoAuthenticationConfigurer 中的 configure 方法,在 configure 方法执行以前加入了 initUserDetailsService 方法,以方便开发展按照本身的方式去初始化 UserDetailsService。不过这里的 initUserDetailsService 方法是空方法。
UserDetailsManagerConfigurer
UserDetailsManagerConfigurer 中实现了 UserDetailsServiceConfigurer 中定义的 initUserDetailsService 方法,具体的实现逻辑就是将 UserDetailsBuilder 所构建出来的 UserDetails 以及提早准备好的 UserDetails 中的用户存储到 UserDetailsService 中。
该类同时添加了 withUser 方法用来添加用户,同时还增长了一个 UserDetailsBuilder 用来构建用户,这些逻辑都比较简单,小伙伴们能够自行查看。
JdbcUserDetailsManagerConfigurer
JdbcUserDetailsManagerConfigurer 在父类的基础上补充了 DataSource 对象,同时还提供了相应的数据库查询方法。
InMemoryUserDetailsManagerConfigurer
InMemoryUserDetailsManagerConfigurer 在父类的基础上重写了构造方法,将父类中的 UserDetailsService 实例定义为 InMemoryUserDetailsManager。
DaoAuthenticationConfigurer
DaoAuthenticationConfigurer 继承自 AbstractDaoAuthenticationConfigurer,只是在构造方法中修改了一下 userDetailsService 而已。
有小伙伴可能要问了,JdbcUserDetailsManagerConfigurer 或者 InMemoryUserDetailsManagerConfigurer,到底在哪里能够用到呀?
松哥给你们举一个简单的例子:
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication().withUser("javaboy")
.password("{noop}123")
.roles("admin");
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.anyRequest().authenticated()
//省略
}
}
复制代码
当你调用 auth.inMemoryAuthentication 进行配置时,实际上调用的就是 InMemoryUserDetailsManagerConfigurer。
这下明白了吧!
AbstractHttpConfigurer 这一派中的东西很是多,咱们全部的过滤器配置,都是它的子类,咱们来看下都有哪些类?
能够看到,它的实现类仍是很是多的。
这么多实现类,松哥就不一一给你们介绍了,我挑一个经常使用的 FormLoginConfigurer 来给你们详细介绍,只要你们把这个理解了,其余的照猫画虎就很好理解了。
咱们一个一个来看。
AbstractHttpConfigurer 继承自 SecurityConfigurerAdapter,并增长了两个方法,disable 和 withObjectPostProcessor:
public abstract class AbstractHttpConfigurer<T extends AbstractHttpConfigurer<T, B>, B extends HttpSecurityBuilder<B>> extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, B> {
/** * Disables the {@link AbstractHttpConfigurer} by removing it. After doing so a fresh * version of the configuration can be applied. * * @return the {@link HttpSecurityBuilder} for additional customizations */
@SuppressWarnings("unchecked")
public B disable() {
getBuilder().removeConfigurer(getClass());
return getBuilder();
}
@SuppressWarnings("unchecked")
public T withObjectPostProcessor(ObjectPostProcessor<?> objectPostProcessor) {
addObjectPostProcessor(objectPostProcessor);
return (T) this;
}
}
复制代码
这两个方法松哥以前都有给你们介绍过,disable 基本上是你们的老熟人了,咱们经常使用的 .csrf().disable()
就是出自这里,那么从这里咱们也能够看到 disable 的实现原理,就是从 getBuilder 中移除相关的 xxxConfigurer,getBuilder 方法获取到的实际上就是 HttpSecurity,因此移除掉 xxxConfigurer 实际上就是从过滤器链中移除掉某一个过滤器,例如 .csrf().disable()
就是移除掉处理 csrf 的过滤器。
另外一个增长的方法是 withObjectPostProcessor,这是为配置类添加手动添加后置处理器的。在 AbstractHttpConfigurer 的父类中其实有一个相似的方法就是 addObjectPostProcessor,可是 addObjectPostProcessor 只是一个添加方法,返回值为 void,而 withObjectPostProcessor 的返回值是当前配置类,也就是 xxxConfigurer,因此若是使用 withObjectPostProcessor 的话,可使用链式配置,事实上,在松哥以前的文章,以及 vhr(github.com/lenve/vhr) 项目中,使用的也都是 withObjectPostProcessor 方法(固然,你也可使用 addObjectPostProcessor,最终效果是同样的)。
AbstractAuthenticationFilterConfigurer 类的功能比较多,源码也是至关至关长。不过咱们只须要抓住两点便可,init 方法和 configure 方法,由于这两个方法是全部 xxxConfigurer 的灵魂。
@Override
public void init(B http) throws Exception {
updateAuthenticationDefaults();
updateAccessDefaults(http);
registerDefaultAuthenticationEntryPoint(http);
}
复制代码
init 方法主要干了三件事:
再来看 configure 方法:
@Override
public void configure(B http) throws Exception {
PortMapper portMapper = http.getSharedObject(PortMapper.class);
if (portMapper != null) {
authenticationEntryPoint.setPortMapper(portMapper);
}
RequestCache requestCache = http.getSharedObject(RequestCache.class);
if (requestCache != null) {
this.defaultSuccessHandler.setRequestCache(requestCache);
}
authFilter.setAuthenticationManager(http
.getSharedObject(AuthenticationManager.class));
authFilter.setAuthenticationSuccessHandler(successHandler);
authFilter.setAuthenticationFailureHandler(failureHandler);
if (authenticationDetailsSource != null) {
authFilter.setAuthenticationDetailsSource(authenticationDetailsSource);
}
SessionAuthenticationStrategy sessionAuthenticationStrategy = http
.getSharedObject(SessionAuthenticationStrategy.class);
if (sessionAuthenticationStrategy != null) {
authFilter.setSessionAuthenticationStrategy(sessionAuthenticationStrategy);
}
RememberMeServices rememberMeServices = http
.getSharedObject(RememberMeServices.class);
if (rememberMeServices != null) {
authFilter.setRememberMeServices(rememberMeServices);
}
F filter = postProcess(authFilter);
http.addFilter(filter);
}
复制代码
configure 中的逻辑就很简答了,构建各类各样的回调函数设置给 authFilter,authFilter 再去 postProcess 中走一圈注册到 Spring 容器中,最后再把 authFilter 添加到过滤器链中。
这即是 AbstractAuthenticationFilterConfigurer 的主要功能。须要提醒你们的是,咱们平常配置的,如:
等方法都是在这里定义的。
最后咱们再来看看 FormLoginConfigurer。
FormLoginConfigurer 在定义是,明确了 AbstractAuthenticationFilterConfigurer 中的泛型是 UsernamePasswordAuthenticationFilter,也就是咱们这里最终要配置的过滤是 UsernamePasswordAuthenticationFilter。
FormLoginConfigurer 重写了 init 方法,配置了一下默认的登陆页面。其余的基本上都是从父类来的,未作太多改变。
另外咱们平常配置的不少东西也是来自这里:
好啦,这就是 FormLoginConfigurer 这个配置类,FormLoginConfigurer 对应的过滤器是 UsernamePasswordAuthenticationFilter,小伙伴们能够自行分析其余的 xxxConfigurer,每个 xxxConfigurer 都对应了一个 不一样的 Filter。
好啦,今天就主要和你们分享一下 SecurityConfigurer 的源码,固然这里还有不少值得再次仔细讨论的东西,松哥将在后面的文章中继续和你们分享。
以为有收获的小伙伴记得点个在看鼓励下松哥哦~