初始化
初始化是在项目启动的阶段完成的。
1. 初始配置
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
这个配置是一切的开始。
2. 初始化结算
DelegatingFilterProxy
DelegatingFilterProxy这个类继承了GenericFilterBean,GenericFilterBean实现了Filter接口。
从本质上来说就是一个Filter,其间接实现了Filter接口,但是在doFilter中其实调用的从Spring 容器中获取到的代理Filter的实现类delegate。
用这个类的好处主要:
- 通过Spring容器来管理servlet filter的生命周期
- 还有就是如果filter中需要一些Spring容器的实例,可以通过spring直接注入,
- 另外读取一些配置文件这些便利的操作都可以通过Spring来配置实现。
Spring web在设计的时候考虑到某些功能的实现是通过Filter来拦截进行实现的,如果直接的简单的实现几个Filter也是可以的,但是Spring框架最核心的是IOC容器,和Spring框架最好的实现就是将要实现的Filter功能注册到IOC容器的一个Bean,这样就可以和Spring IOC容器进行完美的融合,所以Spring Web设计了DelegatingFilterProxy。
1. init方法
在启动项目的时候会执行Filterd的初始化方法。该方法实现在GenericFilterBean中。
@Override
public final void init(FilterConfig filterConfig) throws ServletException {
Assert.notNull(filterConfig, "FilterConfig must not be null");
if (logger.isDebugEnabled()) {
logger.debug("Initializing filter '" + filterConfig.getFilterName() + "'");
}
this.filterConfig = filterConfig;
// Set bean properties from init parameters.
PropertyValues pvs = new FilterConfigPropertyValues(filterConfig, this.requiredProperties);
if (!pvs.isEmpty()) {
try {
BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
ResourceLoader resourceLoader = new ServletContextResourceLoader(filterConfig.getServletContext());
Environment env = this.environment;
if (env == null) {
env = new StandardServletEnvironment();
}
bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, env));
initBeanWrapper(bw);
bw.setPropertyValues(pvs, true);
}
catch (BeansException ex) {
String msg = "Failed to set bean properties on filter '" +
filterConfig.getFilterName() + "': " + ex.getMessage();
logger.error(msg, ex);
throw new NestedServletException(msg, ex);
}
}
// Let subclasses do whatever initialization they like.
initFilterBean(); // 这个方法
if (logger.isDebugEnabled()) {
logger.debug("Filter '" + filterConfig.getFilterName() + "' configured successfully");
}
}
2. initFilterBean方法
在初始化方法中,会执行初始化Filter的方法initFilterBean。这个方法的实现在DelegatingFilterProxy中。
protected void initFilterBean() throws ServletException {
synchronized (this.delegateMonitor) {
if (this.delegate == null) {
// If no target bean name specified, use filter name.
if (this.targetBeanName == null) {
this.targetBeanName = getFilterName();
}
// Fetch Spring root application context and initialize the delegate early,
// if possible. If the root application context will be started after this
// filter proxy, we'll have to resort to lazy initialization.
WebApplicationContext wac = findWebApplicationContext();
if (wac != null) {
this.delegate = initDelegate(wac); //这个方法
}
}
}
}
3. initDelegate方法
在这个初始化方法中又调用initDelegate方法进行初始化。这个方法的实现在DelegatingFilterProxy中。
Filter delegate = (Filter)wac.getBean(targetBeanName, Filter.class);在当前作用域获取对象名字为springSecurityFilterChain的,DelegatingFilterProxy通过springSecurityFilterChain这个名称,得到了一个FilterChainProxy过滤器。
protected Filter initDelegate(WebApplicationContext wac) throws ServletException {
String targetBeanName = this.getTargetBeanName();
Assert.state(targetBeanName != null, "No target bean name set");
Filter delegate = (Filter)wac.getBean(targetBeanName, Filter.class);
if (this.isTargetFilterLifecycle()) {
delegate.init(this.getFilterConfig());
}
return delegate;
}
FilterChainProxy
delegate得到的是FilterChainProxy对象
到此为止,FilterChainProxy过滤器就初始化话好了,FilterChain过滤链也初始化好了。
请求
当一个请求进来的时候,会进入FilterChainProxy执行doFilter方法。
1. doFilter方法
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);
doFilterInternal(request, response, chain);
}
finally {
SecurityContextHolder.clearContext();
request.removeAttribute(FILTER_APPLIED);
}
}
else {
doFilterInternal(request, response, chain);
}
}
不管是否,都执行其doFilterInternal()方法。
2. doFilter方法
private void doFilterInternal(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
FirewalledRequest fwRequest = firewall
.getFirewalledRequest((HttpServletRequest) request);
HttpServletResponse fwResponse = firewall
.getFirewalledResponse((HttpServletResponse) response);
List<Filter> filters = getFilters(fwRequest);
if (filters == null || filters.size() == 0) {
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);
return;
}
VirtualFilterChain vfc = new VirtualFilterChain(fwRequest, chain, filters);
vfc.doFilter(fwRequest, fwResponse);
}
3. getFilters方法
List filters = getFilters(fwRequest);这一行就是返回的过滤器链。
private List<Filter> getFilters(HttpServletRequest request) {
for (SecurityFilterChain chain : filterChains) {
if (chain.matches(request)) {
return chain.getFilters();
}
}
return null;
}
4. VirtualFilterChain.doFilter方法
filter集合执行的逻辑在VirtualFilterChain的doFilter方法中。
private static class VirtualFilterChain implements FilterChain {
private final FilterChain originalChain;
private final List<Filter> additionalFilters;
private final FirewalledRequest firewalledRequest;
private final int size;
private int currentPosition = 0;
private VirtualFilterChain(FirewalledRequest firewalledRequest,
FilterChain chain, List<Filter> additionalFilters) {
this.originalChain = chain;
this.additionalFilters = additionalFilters;
this.size = additionalFilters.size();
this.firewalledRequest = firewalledRequest;
}
@Override
public void doFilter(ServletRequest request, ServletResponse response)
throws IOException, ServletException {
if (currentPosition == size) {
if (logger.isDebugEnabled()) {
logger.debug(UrlUtils.buildRequestUrl(firewalledRequest)
+ " reached end of additional filter chain; proceeding with original chain");
}
// Deactivate path stripping as we exit the security filter chain
this.firewalledRequest.reset();
originalChain.doFilter(request, response);
}
else {
currentPosition++;
Filter nextFilter = additionalFilters.get(currentPosition - 1);
if (logger.isDebugEnabled()) {
logger.debug(UrlUtils.buildRequestUrl(firewalledRequest)
+ " at position " + currentPosition + " of " + size
+ " in additional filter chain; firing Filter: '"
+ nextFilter.getClass().getSimpleName() + "'");
}
nextFilter.doFilter(request, response, this);
}
}
}
filter是如何执行的
为什么一个在VirtualFilterChain的doFilter方法可以执行所有的filter。
执行FilterChainProxy的doFilter方法的时候,传入了初始参数username和age,进入这个方法后,根据position取出相应的Filter,初次进入position是0,执行Filter的doFilter方法,注意,此时Filter的doFilter方法额外传入了一个this参数,这个参数就是初始化的好的FilterChain实例,在Filter中的doFilter的方法中最后又会执行FilterChain的doFilter方法,相当于第二次调用FilterChain实例的doFilter方法,此时posotion是1,然后再执行Filter的doFilter方法,直到所有的Filter执行完,整个执行过程结束。
VirtualFilterChain 类中首先声明了 5 个全局属性:
- originalChain 表示原生的过滤器链,也就是 Web Filter;
- additionalFilters 表示 Spring Security 中的过滤器链;
- firewalledRequest 表示当前请求;
- size 表示过滤器链中过滤器的个数;
- currentPosition 则是过滤器链遍历时候的下标。
VirtualFilterChain.doFilter执行步骤:
- 开始,currentPosition == 0,取出additionalFilters.get(0),得到WebAsyncManagerIntegrationFilter对象,执行其doFilter方法。
- WebAsyncManagerIntegrationFilter.doFilter方法方法会重回VirtualFilterChain.doFilter方法(过滤器链中的所有过滤器的doFilter方法入参都有传入VirtualFilterChain对象,方法的最后都会调用VirtualFilterChain.doFilter方法)。
- 此时currentPosition < size,继续循环执行过滤器链中后续过滤器的doFilter方法。
- 当currentPosition == size,表示过滤器链已经执行完毕,,此时通过调用 originalChain.doFilter 进入到原生过滤链方法中,同时也退出了 Spring Security 过滤器链。
这样就完成了整个过滤器链的执行。