咱們來繼續擼 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 的源碼,固然這裏還有不少值得再次仔細討論的東西,鬆哥將在後面的文章中繼續和你們分享。
以爲有收穫的小夥伴記得點個在看鼓勵下鬆哥哦~