您好,登錄后才能下訂單哦!
這篇文章主要介紹“HttpSecurity是什么”,在日常操作中,相信很多人在HttpSecurity是什么問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”HttpSecurity是什么”的疑惑有所幫助!接下來,請跟著小編一起來學習吧!
首先我們來看下 HttpSecurity 的繼承關系圖:
可以看到,HttpSecurity 繼承自 AbstractConfiguredSecurityBuilder,同時實現了 SecurityBuilder 和 HttpSecurityBuilder 兩個接口。
我們來看下 HttpSecurity 的定義:
public final class HttpSecurity extends
AbstractConfiguredSecurityBuilder<DefaultSecurityFilterChain, HttpSecurity>
implements SecurityBuilder<DefaultSecurityFilterChain>,
HttpSecurityBuilder<HttpSecurity> {
//...
}
這里每一個類都帶有泛型,看得人有點眼花繚亂。
我把這個泛型類拿出來和大家講一下,小伙伴們就明白了。
泛型主要是兩個,DefaultSecurityFilterChain 和 HttpSecurity,HttpSecurity 就不用說了,這是我們今天的主角,那么 DefaultSecurityFilterChain 是干嘛的?
這我們就得從 SecurityFilterChain 說起了。
先來看定義:
public interface SecurityFilterChain {
boolean matches(HttpServletRequest request);
List<Filter> getFilters();
}
SecurityFilterChain 其實就是我們平時所說的 Spring Security 中的過濾器鏈,它里邊定義了兩個方法,一個是 matches 方法用來匹配請求,另外一個 getFilters 方法返回一個 List 集合,集合中放著 Filter 對象,當一個請求到來時,用 matches 方法去比較請求是否和當前鏈吻合,如果吻合,就返回 getFilters 方法中的過濾器,那么當前請求會逐個經過 List 集合中的過濾器。這
SecurityFilterChain 接口只有一個實現類,那就是 DefaultSecurityFilterChain:
public final class DefaultSecurityFilterChain implements SecurityFilterChain {
private static final Log logger = LogFactory.getLog(DefaultSecurityFilterChain.class);
private final RequestMatcher requestMatcher;
private final List<Filter> filters;
public DefaultSecurityFilterChain(RequestMatcher requestMatcher, Filter... filters) {
this(requestMatcher, Arrays.asList(filters));
}
public DefaultSecurityFilterChain(RequestMatcher requestMatcher, List<Filter> filters) {
logger.info("Creating filter chain: " + requestMatcher + ", " + filters);
this.requestMatcher = requestMatcher;
this.filters = new ArrayList<>(filters);
}
public RequestMatcher getRequestMatcher() {
return requestMatcher;
}
public List<Filter> getFilters() {
return filters;
}
public boolean matches(HttpServletRequest request) {
return requestMatcher.matches(request);
}
@Override
public String toString() {
return "[ " + requestMatcher + ", " + filters + "]";
}
}
DefaultSecurityFilterChain 只是對 SecurityFilterChain 中的方法進行了實現,并沒有特別值得說的地方,松哥也就不啰嗦了。
那么從上面的介紹中,大家可以看到,DefaultSecurityFilterChain 其實就相當于是 Spring Security 中的過濾器鏈,一個 DefaultSecurityFilterChain 代表一個過濾器鏈,如果系統中存在多個過濾器鏈,則會存在多個 DefaultSecurityFilterChain 對象。
接下來我們把 HttpSecurity 的這幾個父類捋一捋。
public interface SecurityBuilder<O> {
O build() throws Exception;
}
SecurityBuilder 就是用來構建過濾器鏈的,在 HttpSecurity 實現 SecurityBuilder 時,傳入的泛型就是 DefaultSecurityFilterChain,所以 SecurityBuilder#build 方法的功能很明確,就是用來構建一個過濾器鏈出來。
HttpSecurityBuilder 看名字就是用來構建 HttpSecurity 的。不過它也只是一個接口,具體的實現在 HttpSecurity 中,接口定義如下:
public interface HttpSecurityBuilder<H extends HttpSecurityBuilder<H>> extends
SecurityBuilder<DefaultSecurityFilterChain> {
<C extends SecurityConfigurer<DefaultSecurityFilterChain, H>> C getConfigurer(
Class<C> clazz);
<C extends SecurityConfigurer<DefaultSecurityFilterChain, H>> C removeConfigurer(
Class<C> clazz);
<C> void setSharedObject(Class<C> sharedType, C object);
<C> C getSharedObject(Class<C> sharedType);
H authenticationProvider(AuthenticationProvider authenticationProvider);
H userDetailsService(UserDetailsService userDetailsService) throws Exception;
H addFilterAfter(Filter filter, Class<? extends Filter> afterFilter);
H addFilterBefore(Filter filter, Class<? extends Filter> beforeFilter);
H addFilter(Filter filter);
}
這里的方法比較簡單:
這便是 HttpSecurityBuilder 中的功能,這些接口在 HttpSecurity 中都將得到實現。
AbstractSecurityBuilder 類實現了 SecurityBuilder 接口,該類中主要做了一件事,就是確保整個構建只被構建一次。
public abstract class AbstractSecurityBuilder<O> implements SecurityBuilder<O> {
private AtomicBoolean building = new AtomicBoolean();
private O object;
public final O build() throws Exception {
if (this.building.compareAndSet(false, true)) {
this.object = doBuild();
return this.object;
}
throw new AlreadyBuiltException("This object has already been built");
}
public final O getObject() {
if (!this.building.get()) {
throw new IllegalStateException("This object has not been built");
}
return this.object;
}
protected abstract O doBuild() throws Exception;
}
可以看到,這里重新定義了 build 方法,并設置 build 方法為 final 類型,無法被重寫,在 build 方法中,通過 AtomicBoolean 實現該方法只被調用一次。具體的構建邏輯則定義了新的抽象方法 doBuild,將來在實現類中通過 doBuild 方法定義構建邏輯。
AbstractSecurityBuilder 方法的實現類就是 AbstractConfiguredSecurityBuilder。
AbstractConfiguredSecurityBuilder 中所做的事情就比較多了,我們分別來看。
首先 AbstractConfiguredSecurityBuilder 中定義了一個枚舉類,將整個構建過程分為 5 種狀態,也可以理解為構建過程生命周期的五個階段,如下:
private enum BuildState {
UNBUILT(0),
INITIALIZING(1),
CONFIGURING(2),
BUILDING(3),
BUILT(4);
private final int order;
BuildState(int order) {
this.order = order;
}
public boolean isInitializing() {
return INITIALIZING.order == order;
}
public boolean isConfigured() {
return order >= CONFIGURING.order;
}
}
五種狀態分別是 UNBUILT、INITIALIZING、CONFIGURING、BUILDING 以及 BUILT。另外還提供了兩個判斷方法,isInitializing 判斷是否正在初始化,isConfigured 表示是否已經配置完畢。
AbstractConfiguredSecurityBuilder 中的方法比較多,松哥在這里列出來兩個關鍵的方法和大家分析:
private <C extends SecurityConfigurer<O, B>> void add(C configurer) {
Assert.notNull(configurer, "configurer cannot be null");
Class<? extends SecurityConfigurer<O, B>> clazz = (Class<? extends SecurityConfigurer<O, B>>) configurer
.getClass();
synchronized (configurers) {
if (buildState.isConfigured()) {
throw new IllegalStateException("Cannot apply " + configurer
+ " to already built object");
}
List<SecurityConfigurer<O, B>> configs = allowConfigurersOfSameType ? this.configurers
.get(clazz) : null;
if (configs == null) {
configs = new ArrayList<>(1);
}
configs.add(configurer);
this.configurers.put(clazz, configs);
if (buildState.isInitializing()) {
this.configurersAddedInInitializing.add(configurer);
}
}
}
private Collection<SecurityConfigurer<O, B>> getConfigurers() {
List<SecurityConfigurer<O, B>> result = new ArrayList<>();
for (List<SecurityConfigurer<O, B>> configs : this.configurers.values()) {
result.addAll(configs);
}
return result;
}
第一個就是這個 add 方法,這相當于是在收集所有的配置類。將所有的 xxxConfigure 收集起來存儲到 configurers 中,將來再統一初始化并配置,configurers 本身是一個 LinkedHashMap ,key 是配置類的 class,value 是一個集合,集合里邊放著 xxxConfigure 配置類。當需要對這些配置類進行集中配置的時候,會通過 getConfigurers 方法獲取配置類,這個獲取過程就是把 LinkedHashMap 中的 value 拿出來,放到一個集合中返回。
另一個方法就是 doBuild 方法。
@Override
protected final O doBuild() throws Exception {
synchronized (configurers) {
buildState = BuildState.INITIALIZING;
beforeInit();
init();
buildState = BuildState.CONFIGURING;
beforeConfigure();
configure();
buildState = BuildState.BUILDING;
O result = performBuild();
buildState = BuildState.BUILT;
return result;
}
}
private void init() throws Exception {
Collection<SecurityConfigurer<O, B>> configurers = getConfigurers();
for (SecurityConfigurer<O, B> configurer : configurers) {
configurer.init((B) this);
}
for (SecurityConfigurer<O, B> configurer : configurersAddedInInitializing) {
configurer.init((B) this);
}
}
private void configure() throws Exception {
Collection<SecurityConfigurer<O, B>> configurers = getConfigurers();
for (SecurityConfigurer<O, B> configurer : configurers) {
configurer.configure((B) this);
}
}
在 AbstractSecurityBuilder 類中,過濾器的構建被轉移到 doBuild 方法上面了,不過在 AbstractSecurityBuilder 中只是定義了抽象的 doBuild 方法,具體的實現在 AbstractConfiguredSecurityBuilder。
doBuild 方法就是一邊更新狀態,進行進行初始化。
beforeInit 是一個預留方法,沒有任何實現。
init 方法就是找到所有的 xxxConfigure,挨個調用其 init 方法進行初始化。
beforeConfigure 是一個預留方法,沒有任何實現。
configure 方法就是找到所有的 xxxConfigure,挨個調用其 configure 方法進行配置。
最后則是 performBuild 方法,是真正的過濾器鏈構建方法,但是在 AbstractConfiguredSecurityBuilder 中 performBuild 方法只是一個抽象方法,具體的實現在 HttpSecurity 中。
這便是 HttpSecurity 所有父類、父接口的功能。
看完了父輩,接下來回到我們今天文章的主題,HttpSecurity。
HttpSecurity 做的事情,就是進行各種各樣的 xxxConfigurer 配置。
隨便舉幾例:
public CorsConfigurer<HttpSecurity> cors() throws Exception {
return getOrApply(new CorsConfigurer<>());
}
public CsrfConfigurer<HttpSecurity> csrf() throws Exception {
ApplicationContext context = getContext();
return getOrApply(new CsrfConfigurer<>(context));
}
public ExceptionHandlingConfigurer<HttpSecurity> exceptionHandling() throws Exception {
return getOrApply(new ExceptionHandlingConfigurer<>());
}
HttpSecurity 中有大量類似的方法,過濾器鏈中的過濾器就是這樣一個一個配置的。我就不一一介紹了。
每個配置方法的結尾都會來一句 getOrApply,這個是干嘛的?
private <C extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, HttpSecurity>> C getOrApply(
C configurer) throws Exception {
C existingConfig = (C) getConfigurer(configurer.getClass());
if (existingConfig != null) {
return existingConfig;
}
return apply(configurer);
}
getConfigurer 方法是在它的父類 AbstractConfiguredSecurityBuilder 中定義的,目的就是去查看當前這個 xxxConfigurer 是否已經配置過了。
如果當前 xxxConfigurer 已經配置過了,則直接返回,否則調用 apply 方法,這個 apply 方法最終會調用到 AbstractConfiguredSecurityBuilder#add 方法,將當前配置 configurer 收集起來。
HttpSecurity 中還有一個 addFilter 方法:
public HttpSecurity addFilter(Filter filter) {
Class<? extends Filter> filterClass = filter.getClass();
if (!comparator.isRegistered(filterClass)) {
throw new IllegalArgumentException(
"The Filter class "
+ filterClass.getName()
+ " does not have a registered order and cannot be added without a specified order. Consider using addFilterBefore or addFilterAfter instead.");
}
this.filters.add(filter);
return this;
}
這個 addFilter 方法的作用,主要是在各個 xxxConfigurer 進行配置的時候,會調用到這個方法,(xxxConfigurer 就是用來配置過濾器的),把 Filter 都添加到 fitlers 變量中。
最終在 HttpSecurity 的 performBuild 方法中,構建出來一個過濾器鏈:
@Override
protected DefaultSecurityFilterChain performBuild() {
filters.sort(comparator);
return new DefaultSecurityFilterChain(requestMatcher, filters);
}
先給過濾器排序,然后構造 DefaultSecurityFilterChain 對象。
到此,關于“HttpSecurity是什么”的學習就結束了,希望能夠解決大家的疑惑。理論與實踐的搭配能更好的幫助大家學習,快去試試吧!若想繼續學習更多相關知識,請繼續關注億速云網站,小編會繼續努力為大家帶來更多實用的文章!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。