目录
代理类生成
问题
如下面代码,将JwtAuthenticationFilter注入到spring中,然后通过HttpSecurity对象将Filter添加到SecrityConfig配置中,然后配置的ignoring()不会生效。每次请求还是会走JwtAuthenticationFilter这个Filter。
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Bean(BeanIds.AUTHENTICATION_MANAGER)
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
@Bean
public JwtAuthenticationFilter jwtAuthenticationFilter() {
return new JwtAuthenticationFilter();
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.cors()
.and()
.csrf()
.disable()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.anyRequest()
.permitAll();
http.addFilter(jwtAuthenticationFilter());
}
@Override
public void configure(WebSecurity web) {
web.ignoring().antMatchers("/auth","/");
}
}
查阅了很多资料都没看到有人在源码层面上分析,于是自己根据各个模块源码梳理了下流程。
分析问题
想要解决这个问题,必须了解Secutity的Filter和Spring Filter实现的区别。
阅读源码,让我们一步一步揭开他们的神秘面纱。
服务器的Filter实现原理
注册Filter
这是调用栈,通过debug的方式,让我们分析源码更加简单:

Spring Boot 启动入口
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
SpringApplication中run的主要流程:
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);
this.configureIgnoreBeanInfo(environment);
Banner printedBanner = this.printBanner(environment);
//获取需要刷新的上下文
context = this.createApplicationContext();
exceptionReporters = this.getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[]{ConfigurableApplicationContext.class}, context);
this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);
//刷新上下文
this.refreshContext(context);
this.afterRefresh(context, applicationArguments);
stopWatch.stop();
获取到 AnnotationConfigServletWebServerApplicationContext上下文:
protected ConfigurableApplicationContext createApplicationContext() {
Class<?> contextClass = this.applicationContextClass;
if (contextClass == null) {
try {
switch(this.webApplicationType) {
case SERVLET:
contextClass = Class.forName("org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext");
break;
case REACTIVE:
contextClass = Class.forName("org.springframework.boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext");
break;
default:
contextClass = Class.forName("org.springframework.context.annotation.AnnotationConfigApplicationContext");
}
} catch (ClassNotFoundException var3) {
throw new IllegalStateException("Unable create a default ApplicationContext, please specify an ApplicationContextClass", var3);
}
}
然后到ServletWebServerApplicationContext中调用createWebServer() 方法创建服务器:
protected void onRefresh() {
super.onRefresh();
try {
this.createWebServer();
} catch (Throwable var2) {
throw new ApplicationContextException("Unable to start web server", var2);
}
}
private void createWebServer() {
WebServer webServer = this.webServer;
ServletContext servletContext = this.getServletContext();
if (webServer == null && servletContext == null) {
ServletWebServerFactory factory = this.getWebServerFactory();
this.webServer = factory.getWebServer(new ServletContextInitializer[]{this.getSelfInitializer()});
} else if (servletContext != null) {
try {
this.getSelfInitializer().onStartup(servletContext);
} catch (ServletException var4) {
throw new ApplicationContextException("Cannot initialize servlet context", var4);
}
}
this.initPropertySources();
}
在this.getSelfInitializer()中初始化配置:
private ServletContextInitializer getSelfInitializer() {
return this::selfInitialize;
}
private void selfInitialize(ServletContext servletContext) throws ServletException {
this.prepareWebApplicationContext(servletContext);
this.registerApplicationScope(servletContext);
WebApplicationContextUtils.registerEnvironmentBeans(this.getBeanFactory(), servletContext);
// 将spring bean配置到servlet context中
Iterator var2 = this.getServletContextInitializerBeans().iterator();
while(var2.hasNext()) {
ServletContextInitializer beans = (ServletContextInitializer)var2.next();
beans.onStartup(servletContext);
}
}
ServletContextInitializerBeans在构造函数中将Spring bean中Filter注册到Serlet的上下文中:
public ServletContextInitializerBeans(ListableBeanFactory beanFactory, Class<? extends ServletContextInitializer>... initializerTypes) {
this.initializerTypes = initializerTypes.length != 0 ? Arrays.asList(initializerTypes) : Collections.singletonList(ServletContextInitializer.class);
this.addServletContextInitializerBeans(beanFactory);
this.addAdaptableBeans(beanFactory);
List<ServletContextInitializer> sortedInitializers = (List)this.initializers.values().stream().flatMap((value) -> {
return value.stream().sorted(AnnotationAwareOrderComparator.INSTANCE);
}).collect(Collectors.toList());
this.sortedList = Collections.unmodifiableList(sortedInitializers);
this.logMappings(this.initializers);
}
private void addServletContextInitializerBeans(ListableBeanFactory beanFactory) {
Iterator var2 = this.initializerTypes.iterator();
while(var2.hasNext()) {
Class<? extends ServletContextInitializer> initializerType = (Class)var2.next();
Iterator var4 = this.getOrderedBeansOfType(beanFactory, initializerType).iterator();
while(var4.hasNext()) {
Entry<String, ? extends ServletContextInitializer> initializerBean = (Entry)var4.next();
this.addServletContextInitializerBean((String)initializerBean.getKey(), (ServletContextInitializer)initializerBean.getValue(), beanFactory);
}
}
}
private void addServletContextInitializerBean(String beanName, ServletContextInitializer initializer, ListableBeanFactory beanFactory) {
if (initializer instanceof ServletRegistrationBean) {
Servlet source = ((ServletRegistrationBean)initializer).getServlet();
this.addServletContextInitializerBean(Servlet.class, beanName, initializer, beanFactory, source);
//FilterRegistration用于将filter注册到Serlet的上下文中(Security使用的该方法)
} else if (initializer instanceof FilterRegistrationBean) {
Filter source = ((FilterRegistrationBean)initializer).getFilter();
this.addServletContextInitializerBean(Filter.class, beanName, initializer, beanFactory, source);
} else if (initializer instanceof DelegatingFilterProxyRegistrationBean) {
String source = ((DelegatingFilterProxyRegistrationBean)initializer).getTargetBeanName();
this.addServletContextInitializerBean(Filter.class, beanName, initializer, beanFactory, source);
} else if (initializer instanceof ServletListenerRegistrationBean) {
EventListener source = ((ServletListenerRegistrationBean)initializer).getListener();
this.addServletContextInitializerBean(EventListener.class, beanName, initializer, beanFactory, source);
} else {
this.addServletContextInitializerBean(ServletContextInitializer.class, beanName, initializer, beanFactory, initializer);
}
}
//将Servlet、Filter注册到Servlet容器中
protected void addAdaptableBeans(ListableBeanFactory beanFactory) {
MultipartConfigElement multipartConfig = this.getMultipartConfig(beanFactory);
this.addAsRegistrationBean(beanFactory, Servlet.class, new ServletContextInitializerBeans.ServletRegistrationBeanAdapter(multipartConfig));
this.addAsRegistrationBean(beanFactory, Filter.class, new ServletContextInitializerBeans.FilterRegistrationBeanAdapter());
Iterator var3 = ServletListenerRegistrationBean.getSupportedTypes().iterator();
while(var3.hasNext()) {
Class<?> listenerType = (Class)var3.next();
this.addAsRegistrationBean(beanFactory, EventListener.class, listenerType, new ServletContextInitializerBeans.ServletListenerRegistrationBeanAdapter());
}
}
Filter过滤流程
filter是对servlet进行过滤的,到底它是怎么起到过滤的呢?
通过debug查看调用栈的关系,可以看到Tomcat在接受到request的请求后,会先获取过滤器链:

而后遍历并调用过滤器链,发起过滤流程:
filterChain.doFilter(request.getRequest(), response.getResponse());
Security Filter原理
代理类生成
这里的我们可以看到关于spring security相关的类DelegatingFilterProxyRegistrationBean,这个
DelegatingFilterProxyRegistrationBean就是我们分析security的入口:


Spring Boot 会自动装配SecurityFilterAutoConfiguration这个类,创建DelegatingFilterProxyRegistrationBean bean,代理的目标对象是名为"springSecurityFilterChain"的bean,后续这个"springSecurityFilterChain"会在自动装配的配置文件中创建,后续的过滤入口就在它。
@Configuration
@ConditionalOnWebApplication(type = Type.SERVLET)
@EnableConfigurationProperties(SecurityProperties.class)
@ConditionalOnClass({ AbstractSecurityWebApplicationInitializer.class, SessionCreationPolicy.class })
@AutoConfigureAfter(SecurityAutoConfiguration.class)
public class SecurityFilterAutoConfiguration {
private static final String DEFAULT_FILTER_NAME = AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME;
@Bean
@ConditionalOnBean(name = DEFAULT_FILTER_NAME)
public DelegatingFilterProxyRegistrationBean securityFilterChainRegistration(
SecurityProperties securityProperties) {
DelegatingFilterProxyRegistrationBean registration = new DelegatingFilterProxyRegistrationBean(
DEFAULT_FILTER_NAME);
registration.setOrder(securityProperties.getFilter().getOrder());
registration.setDispatcherTypes(getDispatcherTypes(securityProperties));
return registration;
}
private EnumSet<DispatcherType> getDispatcherTypes(SecurityProperties securityProperties) {
if (securityProperties.getFilter().getDispatcherTypes() == null) {
return null;
}
return securityProperties.getFilter().getDispatcherTypes().stream()
.map((type) -> DispatcherType.valueOf(type.name()))
.collect(Collectors.collectingAndThen(Collectors.toSet(), EnumSet::copyOf));
}
}
Tomcat在启动的时候会调用所有RegistrationBean的onStartup()方法:
public abstract class RegistrationBean implements ServletContextInitializer, Ordered {
public final void onStartup(ServletContext servletContext) throws ServletException {
String description = this.getDescription();
if (!this.isEnabled()) {
logger.info(StringUtils.capitalize(description) + " was not registered (disabled)");
} else {
this.register(description, servletContext);
}
}
}
然后通过父类的AbstractFilterRegistrationBean的getDescription()方法调用到
DelegatingFilterProxyRegistrationBean的getFilter()方法:
protected String getDescription() {
Filter filter = this.getFilter();
Assert.notNull(filter, "Filter must not be null");
return "filter " + this.getOrDeduceName(filter);
}
为Secrity生成一个代理类,后续的过滤在代理类中完成:
public DelegatingFilterProxy getFilter() {
return new DelegatingFilterProxy(this.targetBeanName, this.getWebApplicationContext()) {
protected void initFilterBean() throws ServletException {
}
};
}
配置的注入
Springboot 自动装配了SecurityAutoConfiguration配置类:

然后通过import引入WebSecurityEnablerConfiguration配置,WebSecurityEnablerConfiguration类又开启了@EnableWebSecurity注解,EnableWebSecurity类注解引入了WebSecurityConfiguration配置类。
@Configuration
@ConditionalOnClass(DefaultAuthenticationEventPublisher.class)
@EnableConfigurationProperties(SecurityProperties.class)
@Import({ SpringBootWebSecurityConfiguration.class, WebSecurityEnablerConfiguration.class,
SecurityDataConfiguration.class })
public class SecurityAutoConfiguration {
@Bean
@ConditionalOnMissingBean(AuthenticationEventPublisher.class)
public DefaultAuthenticationEventPublisher authenticationEventPublisher(ApplicationEventPublisher publisher) {
return new DefaultAuthenticationEventPublisher(publisher);
}
}
@Configuration
@ConditionalOnBean(WebSecurityConfigurerAdapter.class)
@ConditionalOnMissingBean(name = BeanIds.SPRING_SECURITY_FILTER_CHAIN)
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
@EnableWebSecurity
public class WebSecurityEnablerConfiguration {
}
@Retention(value = java.lang.annotation.RetentionPolicy.RUNTIME)
@Target(value = { java.lang.annotation.ElementType.TYPE })
@Documented
@Import({ WebSecurityConfiguration.class,
SpringWebMvcImportSelector.class,
OAuth2ImportSelector.class })
@EnableGlobalAuthentication
@Configuration
public @interface EnableWebSecurity {
/**
* Controls debugging support for Spring Security. Default is false.
* @return if true, enables debug support with Spring Security
*/
boolean debug() default false;
}
WebSecurityConfiguration中申明了"springSecurityFilterChain" bean,
@Bean(name = AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME)
public Filter springSecurityFilterChain() throws Exception {
boolean hasConfigurers = webSecurityConfigurers != null
&& !webSecurityConfigurers.isEmpty();
if (!hasConfigurers) {
WebSecurityConfigurerAdapter adapter = objectObjectPostProcessor
.postProcess(new WebSecurityConfigurerAdapter() {
});
webSecurity.apply(adapter);
}
return webSecurity.build();
}
然后调用build()->doBuild()->init()、初始化HttpSecurity配置、初始化WebSecurity配置、初始化过滤器链、忽视的过滤器链。
//省略中间调用
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;
}
}
debug时可以看到生成了五个过滤器的chain,包含四个ignoring()的chain。

performBuild()返回的是一个FilterChainProxy,其实就是一个Filter,下面是类图:
后续的过滤都被FilterChainProxy代理了, 通过遍历调用chain.doFilter(fwRequest, fwResponse)完成请求的过滤:
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
boolean clearContext = request.getAttribute(FILTER_APPLIED) == null;
if (clearContext) {
try {
request.setAttribute(FILTER_APPLIED, Boolean.TRUE);
this.doFilterInternal(request, response, chain);
} finally {
SecurityContextHolder.clearContext();
request.removeAttribute(FILTER_APPLIED);
}
} else {
this.doFilterInternal(request, response, chain);
}
}
private void doFilterInternal(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
FirewalledRequest fwRequest = this.firewall.getFirewalledRequest((HttpServletRequest)request);
HttpServletResponse fwResponse = this.firewall.getFirewalledResponse((HttpServletResponse)response);
List<Filter> filters = this.getFilters((HttpServletRequest)fwRequest);
if (filters != null && filters.size() != 0) {
FilterChainProxy.VirtualFilterChain vfc = new FilterChainProxy.VirtualFilterChain(fwRequest, chain, filters);
vfc.doFilter(fwRequest, fwResponse);
} else {
if (logger.isDebugEnabled()) {
logger.debug(UrlUtils.buildRequestUrl(fwRequest) + (filters == null ? " has no matching filters" : " has an empty filter list"));
}
fwRequest.reset();
chain.doFilter(fwRequest, fwResponse);
}
}
doFilterInternal()中获取匹配的所有过滤器:
List<Filter> filters = this.getFilters((HttpServletRequest)fwRequest);
private void doFilterInternal(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
FirewalledRequest fwRequest = this.firewall.getFirewalledRequest((HttpServletRequest)request);
HttpServletResponse fwResponse = this.firewall.getFirewalledResponse((HttpServletResponse)response);
List<Filter> filters = this.getFilters((HttpServletRequest)fwRequest);
if (filters != null && filters.size() != 0) {
FilterChainProxy.VirtualFilterChain vfc = new FilterChainProxy.VirtualFilterChain(fwRequest, chain, filters);
vfc.doFilter(fwRequest, fwResponse);
} else {
if (logger.isDebugEnabled()) {
logger.debug(UrlUtils.buildRequestUrl(fwRequest) + (filters == null ? " has no matching filters" : " has an empty filter list"));
}
fwRequest.reset();
chain.doFilter(fwRequest, fwResponse);
}
}
在之前的四个忽略的过滤器链中会匹配到,但是Filters是空的。因为在初始化的时候已经根据配置把Filters设置为空了:
private List<Filter> getFilters(HttpServletRequest request) {
Iterator var2 = this.filterChains.iterator();
SecurityFilterChain chain;
do {
if (!var2.hasNext()) {
return null;
}
chain = (SecurityFilterChain)var2.next();
} while(!chain.matches(request));
return chain.getFilters();
}
可以看到我们忽略的请求的过滤器是空的,这时候直接跳过:
Filter注册为bean的问题
如果将我们在security中的filter注册为bean,那么它就是一个全局的过滤器,如上面关于tomcat 是如何加载Filter的分析。
如下图是Tomcat获取的所有过滤器链。FilterBean会被加载到servler上下文,和security 的FilterChainProxy同一等级。
将不会受security config的控制。配置的ignoring()将会失效,每次请求都会走到你自定义的filter中。

所以我们必须new一个对象,把对象交给Security管理,才能时ignoring()生效。
总结
其中涉及到源码模块分别为:
- Spring Boot中服务器的启动流程,这里初始化我们Filter;
- Spring Boot中服务器接受请求,过滤器链调用过程;
- Spring Security自动装配Filter代理类、ignore()等配置信息装配过程;
- Spring Security真实Filters调用过程;
根据上述几点结合源码,通过端点、线程栈的查看基本能清楚其脉络。
下图简要概括了Secutiry的Filter和Spring Filter的Bean的关系:

宠辱不惊 看庭前花开花落
去留无意 看天上云卷云舒
本文深入探讨了Spring Boot应用中过滤器的注册与工作原理,重点分析了Spring Security的Filter实现,包括过滤链的创建、过滤器的忽略配置以及SecurityFilter的代理类生成。在实践中遇到的问题是自定义的JwtAuthenticationFilter未被ignoring()配置忽略,原因是自定义的Filter被注册为Spring Bean导致不受SecurityConfig控制。解决方案是通过理解Spring Security的过滤器链和代理机制,确保自定义过滤器正确地由Security管理。
430

被折叠的 条评论
为什么被折叠?



