目錄
1、過(guò)濾器的視角
2、DelegatingFilterProxy 委派過(guò)濾器代理(類)
2、FilterChainProxy 過(guò)濾器鏈代理(類)
4、SecurityFilterChain 安全過(guò)濾器鏈(接口)
5、Security Filters 安全過(guò)濾器實(shí)例
6、Spring Security 如何處理安全異常?
7、在認(rèn)證的時(shí)候保存用戶請(qǐng)求
????????// 釋義、解讀和思考
1、過(guò)濾器的視角
????????Spring Security 對(duì) Servlet 支持基于 Servlet 過(guò)濾器。下圖顯示了單個(gè)HTTP請(qǐng)求的處理程序的典型分層。
????????客戶端向應(yīng)用程序發(fā)送請(qǐng)求,容器根據(jù)請(qǐng)求 URI 的路徑創(chuàng)建一個(gè) FilterChain,其中包含過(guò)濾器實(shí)例和處理 HttpServletRequest 的 Servlet。// 過(guò)濾器鏈?zhǔn)巾樞驁?zhí)行的,具有有序性
????????因?yàn)?Filter 會(huì)影響下游的 Filter 實(shí)例和 Servlet,所以調(diào)用每個(gè) Filter 的順序非常重要。
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) {
// 省略...
chain.doFilter(request, response);
// 省略...
}
????????// doFilter 方法中有一個(gè)?FilterChain 入?yún)?,F(xiàn)ilterChain 也是一個(gè)接口,既然 FilterChain 是一個(gè)過(guò)濾器鏈,那么它的實(shí)現(xiàn)類中肯定包含有一個(gè)?Filter 列表(持有一個(gè)或多個(gè)Filter 對(duì)象)。看源碼
????????//?Spring Security 利用過(guò)濾器鏈來(lái)實(shí)現(xiàn)身份驗(yàn)證和授權(quán)
2、DelegatingFilterProxy 委派過(guò)濾器代理(類)
????????Spring 提供了一個(gè)名為 DelegatingFilterProxy 的過(guò)濾器代理類,它是 Servlet 容器的生命周期和 Spring 的 ApplicationContext 之間架橋。//這個(gè)類是一個(gè)通用的過(guò)濾器實(shí)例
????????// 在Spring 中,Spring 管理過(guò)濾器的生命周期,所以 Filter 實(shí)例也是 Spring 中的一個(gè)Bean
????????DelegatingFilterProxy 從 ApplicationContext 中查找 Bean Filter-0,然后調(diào)用 Bean Filter-0,下面的代碼展示了這一過(guò)程:
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) {
//獲取Bean
Filter delegate = getFilterBean(someBeanName);
delegate.doFilter(request, response);
}
????????// 總結(jié):DelegatingFilterProxy 這個(gè)類就是一個(gè)過(guò)濾器實(shí)例,Spring 通過(guò)這個(gè)過(guò)濾器,去實(shí)現(xiàn)一系列的認(rèn)證和授權(quán)操作。
????????// 深刻認(rèn)識(shí)下,想想這種做法。
2、FilterChainProxy 過(guò)濾器鏈代理(類)
????????Spring Security 的 Servlet 支持包含在 FilterChainProxy 中。
????????FilterChainProxy 是 Spring Security 提供的一個(gè)特殊過(guò)濾器,它允許通過(guò) SecurityFilterChain 向多個(gè)過(guò)濾器實(shí)例委托。FilterChainProxy 是一個(gè)Bean,它通常被封裝在 DelegatingFilterProxy中。
????????//見(jiàn)名知意,過(guò)濾器鏈的代理 FilterChainProxy 中會(huì)持有過(guò)濾器鏈的對(duì)象
public class FilterChainProxy extends GenericFilterBean {
private static final Log logger = LogFactory.getLog(FilterChainProxy.class);
private final static String FILTER_APPLIED = FilterChainProxy.class.getName().concat(
".APPLIED");
// SecurityFilterChain 對(duì)象列表
private List<SecurityFilterChain> filterChains;
//省略...
}
? ? ? ? // FilterChainProxy ->?DelegatingFilterProxy,可以猜想?DelegatingFilterProxy 中的這個(gè) delegate?是不是就是?FilterChainProxy 的實(shí)例?源碼見(jiàn)分曉
?
public class DelegatingFilterProxy extends GenericFilterBean {
@Nullable
private String contextAttribute;
@Nullable
private WebApplicationContext webApplicationContext;
@Nullable
private String targetBeanName;
private boolean targetFilterLifecycle;
@Nullable
private volatile Filter delegate; //這里有一個(gè)過(guò)濾器委托,proxy
private final Object delegateMonitor;
//省略...
}
4、SecurityFilterChain 安全過(guò)濾器鏈(接口)
????????FilterChainProxy 使用 SecurityFilterChain 來(lái)確定應(yīng)該為當(dāng)前請(qǐng)求調(diào)用哪一個(gè) Spring SecurityFilter 實(shí)例。
????????//選擇過(guò)濾器鏈,在FilterChainProxy 中是這樣做的
// 返回與提供的URL匹配的第一個(gè)過(guò)濾器鏈
private List<Filter> getFilters(HttpServletRequest request) {
for (SecurityFilterChain chain : filterChains) {
if (chain.matches(request)) {
return chain.getFilters();
}
}
return null;
}
//一些源碼:
//SecurityFilterChain(接口) -> DefaultSecurityFilterChain(實(shí)現(xiàn)類) -> 持有RequestMatcher對(duì)象 -> matches方法
//RequestMatcher接口用來(lái)支持匹配HttpServletRequest的簡(jiǎn)單策略。匹配策略有很多,此處不多介紹
//RequestMatcher -> AntPathRequestMatcher(實(shí)現(xiàn)),servlet通過(guò)url路徑進(jìn)行匹配
????????下圖顯示了 SecurityFilterChain 的角色。
????????SecurityFilterChain 中的 Filter 實(shí)例是在 FilterChainProxy 中進(jìn)行注冊(cè)的(而不是?DelegatingFilterProxy)。
public FilterChainProxy(SecurityFilterChain chain) {
this(Arrays.asList(chain));
}
public FilterChainProxy(List<SecurityFilterChain> filterChains) {
this.filterChains = filterChains;
}
????????使用 FilterChainProxy 作為起點(diǎn)的優(yōu)勢(shì)://這些特點(diǎn)很重要,仔細(xì)看
- 這樣做為 Spring Security 的所有 Servlet 支持提供了一個(gè)起點(diǎn),如果試圖對(duì) Spring Security 的 Servlet 支持進(jìn)行故障排除,那么在 FilterChainProxy 中添加一個(gè)調(diào)試點(diǎn)是一個(gè)很好的開(kāi)始。//所有的過(guò)濾器都在這里進(jìn)行注冊(cè),然后開(kāi)始調(diào)用,過(guò)濾器的調(diào)用從FilterChainProxy選擇一個(gè)過(guò)濾器鏈開(kāi)始
- FilterChainProxy 是 Spring Security 使用的核心,它可以執(zhí)行一些不可選的任務(wù)。例如,清除 SecurityContext 以避免內(nèi)存泄漏,應(yīng)用 Spring Security 的HttpFirewall 來(lái)保護(hù)應(yīng)用程序免受某些類型的攻擊。//不可選的任務(wù),就是必須做的一些事情或者一些通用的功能
- 在確定何時(shí)調(diào)用 SecurityFilterChain 方面提供了更大的靈活性。在 Servlet 容器中,僅根據(jù) URL 調(diào)用 Filter 實(shí)例。然而,F(xiàn)ilterChainProxy 可以通過(guò)使用 RequestMatcher 接口,基于 HttpServletRequest 中的任何內(nèi)容來(lái)確定調(diào)用。//方便做選擇,RequestMatcher -> 匹配策略接口
????????下圖顯示了多個(gè) SecurityFilterChain 實(shí)例:
????????在 Multiple SecurityFilterChain 圖中,F(xiàn)ilterChainProxy 決定應(yīng)該使用哪個(gè) SecurityFilterChain。
????????這里有一些匹配規(guī)則:?
????????FilterChainProxy 只會(huì)調(diào)用匹配到的第一個(gè) SecurityFilterChain。如請(qǐng)求 URL 為:?/api/messages/,它首先匹配的是 SecurityFilterChain-0(匹配規(guī)則:/api/**),因此只調(diào)用 SecurityFilterChain-0,即使它也匹配 SecurityFilterChain-n(匹配規(guī)則:/**)。//執(zhí)行第一個(gè)匹配到的過(guò)濾器鏈
????????如果請(qǐng)求 URL 為 /messages/ ,它與SecurityFilterChain-0(匹配規(guī)則:/api/**)不匹配,因此 FilterChainProxy 會(huì)繼續(xù)嘗試每個(gè) SecurityFilterChain。假設(shè)沒(méi)有其他 SecurityFilterChain 實(shí)例匹配,則調(diào)用 SecurityFilterChain-n。
????????需要注意的是,每個(gè) SecurityFilterChain 都是唯一的,并且可以進(jìn)行單獨(dú)配置。如果應(yīng)用程序希望 Spring security 忽略某些請(qǐng)求,那么可以在?SecurityFilterChain 中不設(shè)置任何的安全過(guò)濾器實(shí)例。//SecurityFilterChain 這個(gè)里邊可以沒(méi)有任何過(guò)濾器,那就是不進(jìn)行任何攔截
5、Security Filters 安全過(guò)濾器實(shí)例
????????以下是 Spring Security 的過(guò)濾器排序的綜合列表://按順序排列的
- ForceEagerSessionCreationFilter
- ChannelProcessingFilter
- WebAsyncManagerIntegrationFilter
- SecurityContextPersistenceFilter
- HeaderWriterFilter
- CorsFilter
- CsrfFilter
- LogoutFilter
- OAuth2AuthorizationRequestRedirectFilter
- Saml2WebSsoAuthenticationRequestFilter
- X509AuthenticationFilter
- AbstractPreAuthenticatedProcessingFilter
- CasAuthenticationFilter
- OAuth2LoginAuthenticationFilter
- Saml2WebSsoAuthenticationFilter
- UsernamePasswordAuthenticationFilter
- DefaultLoginPageGeneratingFilter
- DefaultLogoutPageGeneratingFilter
- ConcurrentSessionFilter
- DigestAuthenticationFilter
- BearerTokenAuthenticationFilter
- BasicAuthenticationFilter
- RequestCacheAwareFilter
- SecurityContextHolderAwareRequestFilter
- JaasApiIntegrationFilter
- RememberMeAuthenticationFilter
- AnonymousAuthenticationFilter
- OAuth2AuthorizationCodeGrantFilter
- SessionManagementFilter
- ExceptionTranslationFilter? //異常處理過(guò)濾器
- FilterSecurityInterceptor
- SwitchUserFilter
????????//這些過(guò)濾器的排序是Spring官方提供的,后來(lái)Spring可能覺(jué)得這樣展示的意義不大,刪去了這部分內(nèi)容,?補(bǔ)充了一些從日志查看過(guò)濾器加載順序的說(shuō)明。(過(guò)濾器的加載順序會(huì)在Info日志中打印,以實(shí)時(shí)加載為準(zhǔn))。
6、Spring Security 如何處理安全異常?
????????//首先說(shuō),這里就是 Spring Security 的工作原理,重中之重,非常重要,雖然講的是一個(gè)異常過(guò)濾器,但是實(shí)際上是一個(gè)執(zhí)行流程。
????????ExceptionTranslationFilter 允許將 AccessDeniedException 和 AuthenticationException 轉(zhuǎn)換為 HTTP 響應(yīng)。//兩個(gè)異常,禁止訪問(wèn) + 認(rèn)證異常
????????ExceptionTranslationFilter 作為安全過(guò)濾器之一,自動(dòng)插入到 FilterChainProxy 中。
????????下圖顯示了 ExceptionTranslationFilter 與其他組件的關(guān)系:
? ? ? ? 首先,ExceptionTranslationFilter 調(diào)用 FilterChain.doFilter(request, response)?來(lái)調(diào)用應(yīng)用程序的剩余部分,這里有兩種情況,用戶未經(jīng)過(guò)認(rèn)證以及用戶被拒絕訪問(wèn)。// Continue ProcessingRequest Normally -> 繼續(xù)正常處理請(qǐng)求
? ? ? ? 如果用戶未經(jīng)過(guò)身份驗(yàn)證或者拋出了 AuthenticationException,則啟動(dòng)身份驗(yàn)證。
- 首先清除 SecurityContextHolder,這個(gè)是一個(gè) Spring Security 中的一個(gè)核心類。
- RequestCache:保存 HttpServletRequest 請(qǐng)求,在身份驗(yàn)證成功后可以使用它重復(fù)原始請(qǐng)求。//也可以不保存,那么用戶就需要重新去請(qǐng)求
- AuthenticationEntryPoint?用于從客戶端獲取請(qǐng)求憑據(jù)。例如,它可能重定向到登錄頁(yè)面或發(fā)送 WWW-Authenticate 報(bào)文頭。//AuthenticationEntryPoint(接口)?-> 比如重定向到登錄頁(yè)面,要求登錄,執(zhí)行從客戶端獲取請(qǐng)求憑據(jù)的策略
????????//以上這三個(gè)類都給出了身份驗(yàn)證的大致流程,流程中給出了這些關(guān)鍵類的使用時(shí)機(jī),描述了?Spring Security 的大致框架。
????????如果是 AccessDeniedException,則拒絕訪問(wèn)。直接調(diào)用 AccessDeniedHandler 來(lái)處理拒絕訪問(wèn)。//?AccessDeniedHandler(接口),也可以自定義處理策略
????????如果應(yīng)用程序沒(méi)有拋出 AccessDeniedException 或 AuthenticationException,則ExceptionTranslationFilter 不做任何事情。//這個(gè)過(guò)濾器只處理這兩個(gè)異常
????????ExceptionTranslationFilter 的偽代碼邏輯如下:
try {
filterChain.doFilter(request, response);
} catch (AccessDeniedException | AuthenticationException ex) { //捕獲特定異常
if (!authenticated || ex instanceof AuthenticationException) {
startAuthentication(); //啟動(dòng)認(rèn)證流程
} else {
accessDenied(); //決絕策略
}
}
7、在認(rèn)證的時(shí)候保存用戶請(qǐng)求
????????當(dāng)用戶請(qǐng)求資源時(shí),如果沒(méi)有經(jīng)過(guò)身份驗(yàn)證,就需要保存請(qǐng)求信息,當(dāng)身份驗(yàn)證成功后再重新執(zhí)行該請(qǐng)求。在 Spring Security 中,通過(guò)使用 RequestCache 的實(shí)現(xiàn)來(lái)保存 HttpServletRequest。//RequestCache是一個(gè)接口,請(qǐng)求緩存策略可以有不同的實(shí)現(xiàn)
????????RequestCacheAwareFilter 和?RequestCache 接口
????????HttpServletRequest 保存在 RequestCache 中。當(dāng)用戶成功通過(guò)身份驗(yàn)證時(shí),將使用 RequestCache 重新執(zhí)行原始請(qǐng)求。
????????RequestCacheAwareFilter 使用 RequestCache 來(lái)保存 HttpServletRequest。默認(rèn)情況下,使用 HttpSessionRequestCache。
????????下面的代碼演示了如何定制 RequestCache 的實(shí)現(xiàn),該實(shí)現(xiàn)用于在參數(shù) continue 存在的情況下檢查保存請(qǐng)求的 HttpSession。//這里存儲(chǔ)的是一個(gè)標(biāo)記,通過(guò)RequestCacheAwareFilter過(guò)濾器使用
@Bean
DefaultSecurityFilterChain springSecurity(HttpSecurity http) throws Exception {
HttpSessionRequestCache requestCache = new HttpSessionRequestCache();
requestCache.setMatchingRequestParameterName("continue");
http
// ...
.requestCache((cache) -> cache.requestCache(requestCache));
return http.build();
}
????????如果不希望在會(huì)話中存儲(chǔ)用戶未經(jīng)身份驗(yàn)證的請(qǐng)求,可以使用 NullRequestCache 實(shí)現(xiàn)。//不保存會(huì)話信息,直接重定向到登錄頁(yè)文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-602744.html
@Bean
SecurityFilterChain springSecurity(HttpSecurity http) throws Exception {
RequestCache nullRequestCache = new NullRequestCache();
http
// ...
.requestCache((cache) -> cache.requestCache(nullRequestCache));
return http.build();
}
????????至此,全文結(jié)束。文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-602744.html
到了這里,關(guān)于Spring Security 的工作原理/總體架構(gòu)的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!