1 場(chǎng)景
SpringSecurity
起作用的原理依賴(lài)于FilterChainProxy
類(lèi),其本身是個(gè)Servlet中的Filter
,其中內(nèi)置了攔截器鏈
來(lái)對(duì)請(qǐng)求進(jìn)行層層攔截,因此達(dá)到安全驗(yàn)證的目的。
本文主要根據(jù)源碼分析下扒接,在SpringBoot的環(huán)境中springSecurity中的攔截器,如何達(dá)到安全驗(yàn)證的目的们衙?
2 相關(guān)版本
此處的源碼依賴(lài)SpringBoot的版本:2.3.3.RELEASE
钾怔,相關(guān)依賴(lài)如下:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
3 基本原理
3.1 組件之前的關(guān)系
FilterChainProxy
作為一個(gè)Filter
,并沒(méi)有直接配置在servlet中蒙挑,而是借助了DelegatingFilterProxy
對(duì)象進(jìn)行了一層代理
宗侦。
其中之間的關(guān)系如下所示(此圖來(lái)自spring官網(wǎng)):
如上圖所示,DelegatingFilterProxy也是Filter
忆蚀,其配置到Servlet的Filter中凝垛,對(duì)請(qǐng)求進(jìn)行攔截。
攔截到請(qǐng)求后蜓谋,執(zhí)行doFilter
方法時(shí)梦皮,真正執(zhí)行方法的,是被代理的對(duì)象FilterChainProxy(bean的name為springSecurityFilterChain)中的doFilter
方法桃焕。如下圖:
實(shí)現(xiàn)代碼如下:
public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
// 懶加載被代理對(duì)象
Filter delegateToUse = this.delegate;
if (delegateToUse == null) {
synchronized (this.delegateMonitor) {
delegateToUse = this.delegate;
if (delegateToUse == null) {
WebApplicationContext wac = findWebApplicationContext();
if (wac == null) {
throw new IllegalStateException("No WebApplicationContext found: " +
"no ContextLoaderListener or DispatcherServlet registered?");
}
delegateToUse = initDelegate(wac);
}
this.delegate = delegateToUse;
}
}
// 被代理對(duì)象(FilterChainProxy)執(zhí)行真正的doFilter方法
invokeDelegate(delegateToUse, request, response, filterChain);
}
// 初始化代理對(duì)象
protected Filter initDelegate(WebApplicationContext wac) throws ServletException {
// 此處的targetName值為“springSecurityFilterChain”
String targetBeanName = getTargetBeanName();
Assert.state(targetBeanName != null, "No target bean name set");
// 映射的被代理的bean類(lèi)型為“FilterChainProxy”剑肯,name為“springSecurityFilterChain”
Filter delegate = wac.getBean(targetBeanName, Filter.class);
if (isTargetFilterLifecycle()) {
delegate.init(getFilterConfig());
}
return delegate;
}
protected void invokeDelegate(
Filter delegate, ServletRequest request, ServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
// 被代理對(duì)象(springSecurityFilterChain),執(zhí)行真正的doFilter方法
delegate.doFilter(request, response, filterChain);
}
3.2 為什么借助DelegatingFilterProxy進(jìn)行代理
使用DelegatingFilterProxy代理的Filter观堂,可以受spring上下文環(huán)境管理让网,相對(duì)于傳統(tǒng)的Filter呀忧,有如下優(yōu)點(diǎn):
- 可以使用spring上下文環(huán)境中的bean
- 可以很方便使用spring加載配置文件
- 可以用spring管理Filter的生命周期(默認(rèn)關(guān)閉,可設(shè)置
targetFilterLifecycle
為true開(kāi)啟)
4 源碼分析
SpringBoot加載FilterChainProxy的代碼調(diào)用關(guān)系如下圖:
下文將對(duì)上圖的代碼結(jié)構(gòu)圖中標(biāo)記位置①溃睹、②等進(jìn)行詳細(xì)記錄
- 代碼①
private void createWebServer() {
WebServer webServer = this.webServer;
ServletContext servletContext = getServletContext();
if (webServer == null && servletContext == null) {
ServletWebServerFactory factory = getWebServerFactory();
// getSelfInitializer調(diào)用位置③代碼
this.webServer = factory.getWebServer(getSelfInitializer());
getBeanFactory().registerSingleton("webServerGracefulShutdown",
new WebServerGracefulShutdownLifecycle(this.webServer));
getBeanFactory().registerSingleton("webServerStartStop",
new WebServerStartStopLifecycle(this, this.webServer));
}
else if (servletContext != null) {
try {
// 調(diào)用位置②代碼
getSelfInitializer().onStartup(servletContext);
}
catch (ServletException ex) {
throw new ApplicationContextException("Cannot initialize servlet context", ex);
}
}
initPropertySources();
}
- 代碼②
// 代碼①中的
getSelfInitializer().onStartup(servletContext);
- 代碼③
public ServletContextInitializerBeans(ListableBeanFactory beanFactory,
Class<? extends ServletContextInitializer>... initializerTypes) {
this.initializers = new LinkedMultiValueMap<>();
this.initializerTypes = (initializerTypes.length != 0) ? Arrays.asList(initializerTypes)
: Collections.singletonList(ServletContextInitializer.class);
addServletContextInitializerBeans(beanFactory);
addAdaptableBeans(beanFactory);
List<ServletContextInitializer> sortedInitializers = this.initializers.values().stream()
.flatMap((value) -> value.stream().sorted(AnnotationAwareOrderComparator.INSTANCE))
.collect(Collectors.toList());
this.sortedList = Collections.unmodifiableList(sortedInitializers);
logMappings(this.initializers);
}
- 代碼④
private void addServletContextInitializerBeans(ListableBeanFactory beanFactory) {
for (Class<? extends ServletContextInitializer> initializerType : this.initializerTypes) {
for (Entry<String, ? extends ServletContextInitializer> initializerBean : getOrderedBeansOfType(beanFactory,
initializerType)) {
addServletContextInitializerBean(initializerBean.getKey(), initializerBean.getValue(), beanFactory);
}
}
}
- 代碼⑤
private <T> List<Entry<String, T>> getOrderedBeansOfType(ListableBeanFactory beanFactory, Class<T> type,
Set<?> excludes) {
String[] names = beanFactory.getBeanNamesForType(type, true, false);
Map<String, T> map = new LinkedHashMap<>();
for (String name : names) {
if (!excludes.contains(name) && !ScopedProxyUtils.isScopedTarget(name)) {
T bean = beanFactory.getBean(name, type);
if (!excludes.contains(bean)) {
map.put(name, bean);
}
}
}
List<Entry<String, T>> beans = new ArrayList<>(map.entrySet());
beans.sort((o1, o2) -> AnnotationAwareOrderComparator.INSTANCE.compare(o1.getValue(), o2.getValue()));
return beans;
}
- 代碼⑥
private void addServletContextInitializerBean(String beanName, ServletContextInitializer initializer,
ListableBeanFactory beanFactory) {
if (initializer instanceof ServletRegistrationBean) {
Servlet source = ((ServletRegistrationBean<?>) initializer).getServlet();
addServletContextInitializerBean(Servlet.class, beanName, initializer, beanFactory, source);
}
else if (initializer instanceof FilterRegistrationBean) {
Filter source = ((FilterRegistrationBean<?>) initializer).getFilter();
addServletContextInitializerBean(Filter.class, beanName, initializer, beanFactory, source);
}
else if (initializer instanceof DelegatingFilterProxyRegistrationBean) {
String source = ((DelegatingFilterProxyRegistrationBean) initializer).getTargetBeanName();
addServletContextInitializerBean(Filter.class, beanName, initializer, beanFactory, source);
}
else if (initializer instanceof ServletListenerRegistrationBean) {
EventListener source = ((ServletListenerRegistrationBean<?>) initializer).getListener();
addServletContextInitializerBean(EventListener.class, beanName, initializer, beanFactory, source);
}
else {
addServletContextInitializerBean(ServletContextInitializer.class, beanName, initializer, beanFactory,
initializer);
}
}
- 代碼⑦
private void addServletContextInitializerBean(Class<?> type, String beanName, ServletContextInitializer initializer,
ListableBeanFactory beanFactory, Object source) {
this.initializers.add(type, initializer);
if (source != null) {
// Mark the underlying source as seen in case it wraps an existing bean
this.seen.add(source);
}
if (logger.isTraceEnabled()) {
String resourceDescription = getResourceDescription(beanName, beanFactory);
int order = getOrder(initializer);
logger.trace("Added existing " + type.getSimpleName() + " initializer bean '" + beanName + "'; order="
+ order + ", resource=" + resourceDescription);
}
}
- 代碼⑧
public final void onStartup(ServletContext servletContext) throws ServletException {
String description = getDescription();
if (!isEnabled()) {
logger.info(StringUtils.capitalize(description) + " was not registered (disabled)");
return;
}
register(description, servletContext);
}
- 代碼⑨
// 類(lèi)AbstractFilterRegistrationBean中的方法
protected String getDescription() {
Filter filter = getFilter();
Assert.notNull(filter, "Filter must not be null");
return "filter " + getOrDeduceName(filter);
}
- 代碼⑩
// 重要
public DelegatingFilterProxy getFilter() {
return new DelegatingFilterProxy(this.targetBeanName, getWebApplicationContext()) {
@Override
protected void initFilterBean() throws ServletException {
// Don't initialize filter bean on init()
}
};
}
- 代碼(11)
protected final void register(String description, ServletContext servletContext) {
D registration = addRegistration(description, servletContext);
if (registration == null) {
logger.info(StringUtils.capitalize(description) + " was not registered (possibly already registered?)");
return;
}
configure(registration);
}
- 代碼(12)
// Filter被加入到servlet環(huán)境中
protected Dynamic addRegistration(String description, ServletContext servletContext) {
Filter filter = getFilter();
return servletContext.addFilter(getOrDeduceName(filter), filter);
}