SpringSecurity源碼分析-過濾器鏈是如何植入到spring中的
一切的源頭都是因為在web.xml中配置了這樣一個Filter
<!--security--><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>
DelegatingFilterProxy中加載FilterChainProxy
- 這是一個模板類,是一個過濾器鏈代理對象,SpringSecurity中的FilterChainProxy通過這個過濾器植入進來
- DelegatingFilterProxy是對外提供的,用于植入外部過濾器鏈的類
- FilterChainProxy在這個類中被植入Spring中.
public class DelegatingFilterProxy extends GenericFilterBean {@Nullableprivate String contextAttribute;@Nullableprivate WebApplicationContext webApplicationContext;@Nullableprivate String targetBeanName;private boolean targetFilterLifecycle;@Nullable/** 真實的過濾器 */private volatile Filter delegate;private final Object delegateMonitor;public DelegatingFilterProxy() {this.targetFilterLifecycle = false;this.delegateMonitor = new Object();}public DelegatingFilterProxy(Filter delegate) {this.targetFilterLifecycle = false;this.delegateMonitor = new Object();Assert.notNull(delegate, "Delegate Filter must not be null");this.delegate = delegate;}public DelegatingFilterProxy(String targetBeanName) {this(targetBeanName, (WebApplicationContext)null);}public DelegatingFilterProxy(String targetBeanName, @Nullable WebApplicationContext wac) {this.targetFilterLifecycle = false;this.delegateMonitor = new Object();Assert.hasText(targetBeanName, "Target Filter bean name must not be null or empty");this.setTargetBeanName(targetBeanName);this.webApplicationContext = wac;if (wac != null) {this.setEnvironment(wac.getEnvironment());}}public void setContextAttribute(@Nullable String contextAttribute) {this.contextAttribute = contextAttribute;}@Nullablepublic String getContextAttribute() {return this.contextAttribute;}public void setTargetBeanName(@Nullable String targetBeanName) {this.targetBeanName = targetBeanName;}@Nullableprotected String getTargetBeanName() {return this.targetBeanName;}public void setTargetFilterLifecycle(boolean targetFilterLifecycle) {this.targetFilterLifecycle = targetFilterLifecycle;}protected boolean isTargetFilterLifecycle() {return this.targetFilterLifecycle;}/** 這個方法在父類的init方法中調用,這個方法構造真實的過濾器*/protected void initFilterBean() throws ServletException {synchronized(this.delegateMonitor) {if (this.delegate == null) {if (this.targetBeanName == null) {this.targetBeanName = this.getFilterName();}WebApplicationContext wac = this.findWebApplicationContext();if (wac != null) {this.delegate = this.initDelegate(wac);}}}}public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws ServletException, IOException {Filter delegateToUse = this.delegate;if (delegateToUse == null) {synchronized(this.delegateMonitor) {delegateToUse = this.delegate;if (delegateToUse == null) {WebApplicationContext wac = this.findWebApplicationContext();if (wac == null) {throw new IllegalStateException("No WebApplicationContext found: no ContextLoaderListener or DispatcherServlet registered?");}delegateToUse = this.initDelegate(wac);}this.delegate = delegateToUse;}}/** 調用真實過濾器中的doFilter方法*/this.invokeDelegate(delegateToUse, request, response, filterChain);}public void destroy() {Filter delegateToUse = this.delegate;if (delegateToUse != null) {this.destroyDelegate(delegateToUse);}}@Nullableprotected WebApplicationContext findWebApplicationContext() {if (this.webApplicationContext != null) {if (this.webApplicationContext instanceof ConfigurableApplicationContext) {ConfigurableApplicationContext cac = (ConfigurableApplicationContext)this.webApplicationContext;if (!cac.isActive()) {cac.refresh();}}return this.webApplicationContext;} else {String attrName = this.getContextAttribute();return attrName != null ? WebApplicationContextUtils.getWebApplicationContext(this.getServletContext(), attrName) : WebApplicationContextUtils.findWebApplicationContext(this.getServletContext());}}/** 獲取過濾器鏈 springSecurityFilterChain*/protected Filter initDelegate(WebApplicationContext wac) throws ServletException {String targetBeanName = this.getTargetBeanName();Assert.state(targetBeanName != null, "No target bean name set");/** 根據名字和類型獲取獲取過濾器鏈,這里的targetBeanName即springSecurityFilterChain,這個是在Spring中寫死的,自動裝配的..*/Filter delegate = (Filter)wac.getBean(targetBeanName, Filter.class);if (this.isTargetFilterLifecycle()) {delegate.init(this.getFilterConfig());}return delegate;}protected void invokeDelegate(Filter delegate, ServletRequest request, ServletResponse response, FilterChain filterChain) throws ServletException, IOException {delegate.doFilter(request, response, filterChain);}protected void destroyDelegate(Filter delegate) {if (this.isTargetFilterLifecycle()) {delegate.destroy();}}
}
小結:通過過濾器鏈代理對象DelegatingFilterProxy找到真正的過濾器鏈FilterChainProxy,然后去一個一個的過濾器去處理請求
FilterChainProxy
- private List filterChains 存儲過濾器鏈
- doFilter方法中調用doFilterInternal方法
- 獲取到所有的過濾器,挨個執行filter的filter方法
public class FilterChainProxy extends GenericFilterBean {// ~ Static fields/initializers// =====================================================================================private static final Log logger = LogFactory.getLog(FilterChainProxy.class);// ~ Instance fields// ================================================================================================private final static String FILTER_APPLIED = FilterChainProxy.class.getName().concat(".APPLIED");/**存儲過濾器鏈**/private List<SecurityFilterChain> filterChains;private FilterChainValidator filterChainValidator = new NullFilterChainValidator();private HttpFirewall firewall = new StrictHttpFirewall();// ~ Methods// ========================================================================================================public FilterChainProxy() {}public FilterChainProxy(SecurityFilterChain chain) {this(Arrays.asList(chain));}public FilterChainProxy(List<SecurityFilterChain> filterChains) {this.filterChains = filterChains;}@Overridepublic void afterPropertiesSet() {filterChainValidator.validate(this);}@Overridepublic 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);}}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);}/*** Returns the first filter chain matching the supplied URL.** @param request the request to match* @return an ordered array of Filters defining the filter chain*/private List<Filter> getFilters(HttpServletRequest request) {for (SecurityFilterChain chain : filterChains) {if (chain.matches(request)) {return chain.getFilters();}}return null;}/*** Convenience method, mainly for testing.** @param url the URL* @return matching filter list*/public List<Filter> getFilters(String url) {return getFilters(firewall.getFirewalledRequest((new FilterInvocation(url, "GET").getRequest())));}/*** @return the list of {@code SecurityFilterChain}s which will be matched against and* applied to incoming requests.*/public List<SecurityFilterChain> getFilterChains() {return Collections.unmodifiableList(filterChains);}/*** Used (internally) to specify a validation strategy for the filters in each* configured chain.** @param filterChainValidator the validator instance which will be invoked on during* initialization to check the {@code FilterChainProxy} instance.*/public void setFilterChainValidator(FilterChainValidator filterChainValidator) {this.filterChainValidator = filterChainValidator;}/*** Sets the "firewall" implementation which will be used to validate and wrap (or* potentially reject) the incoming requests. The default implementation should be* satisfactory for most requirements.** @param firewall*/public void setFirewall(HttpFirewall firewall) {this.firewall = firewall;}@Overridepublic String toString() {StringBuilder sb = new StringBuilder();sb.append("FilterChainProxy[");sb.append("Filter Chains: ");sb.append(filterChains);sb.append("]");return sb.toString();}// ~ Inner Classes// ==================================================================================================/*** Internal {@code FilterChain} implementation that is used to pass a request through* the additional internal list of filters which match the request.*/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;}@Overridepublic 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 chainthis.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);}}}public interface FilterChainValidator {void validate(FilterChainProxy filterChainProxy);}private static class NullFilterChainValidator implements FilterChainValidator {@Overridepublic void validate(FilterChainProxy filterChainProxy) {}}}