visionzheng 2020-04-29
配置代码
@Bean
public ShiroFilterFactoryBean (@Qualifier("securityManager") SecurityManager securityManager) {
log.info("进入shiroFilter");
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
shiroFilterFactoryBean.setSecurityManager(securityManager);
// 拦截器
Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
filterChainDefinitionMap.put("/static/**", "anon");
filterChainDefinitionMap.put("/user/login", "anon");
filterChainDefinitionMap.put("/user/logout", "logout");
// 根据用户的角色赋予相应的权限
// filterChainDefinitionMap.put("/add", "roles[admin]");
// filterChainDefinitionMap.put("/delete", "roles[admin]");
// filterChainDefinitionMap.put("/delete", "roles[author]");
filterChainDefinitionMap.put("/addPermission", "roles[author]");
filterChainDefinitionMap.put("/add", "perms[user:add]");
filterChainDefinitionMap.put("/delete", "perms[user:delete]");
filterChainDefinitionMap.put("/userList", "perms[user:list]");
// 匹配所有的路径
// 通过Map集合组成了一个拦截器链 ,自顶向下过滤,一旦匹配,则不再执行下面的过滤
// 如果下面的定义与上面冲突,那按照了谁先定义谁说了算
// 一定要配置在最后
filterChainDefinitionMap.put("/**", "authc");
// 将拦截器链设置到shiro中
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
// 如果不设置默认会自动寻找Web工程根目录下的"/login.html"页面
shiroFilterFactoryBean.setLoginUrl("/login");
// 登录成功后要跳转的链接
shiroFilterFactoryBean.setSuccessUrl("/index");
// 未授权页面
shiroFilterFactoryBean.setUnauthorizedUrl("/403");
return shiroFilterFactoryBean;
}继承链

ShiroFilterFactoryBean实现了FactoryBean和BeanPostProcessor接口
1、BeanPostProcessor接口的实现
/**
* Inspects a bean, and if it implements the {@link Filter} interface, automatically adds that filter
* instance to the internal {@link #setFilters(java.util.Map) filters map} that will be referenced
* later during filter chain construction.
*/
// 检查一个bean,如果它实现了filter接口,自动将该过滤器添加到内部filters map中,稍后在过滤器链的构建过程中再引用该实例(后面第4点createFilterChainManager )
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof Filter) {
log.debug("Found filter chain candidate filter ‘{}‘", beanName);
Filter filter = (Filter) bean;
// 应用全局配置
applyGlobalPropertiesIfNecessary(filter);
// 将在Spring中注册(而不是在ShiroFilterFactoryBean中配置的)的Filter并入.
// 这里面就牵扯出一个有趣的问题 : FactoryBean<T>接口的getObject方法和 BeanPostProcessor接口的postProcessBeforeInitialization的执行先后顺序?
// (为了保证不遗漏Filter, 我们可以猜测后者必须优先于前者。)
// 上面这个括号里面的参考下面博客的说法有一点要注意的是,打断点调试确实是postProcessBeforeInitialization先进去的但是并没有进去找到任何的filter,因为我是注解提供的filterMap,所以是在执行createFilterChainManager的时候先查找default的filter再添加我们在config类中传进去的flterMap解析filter到filterChainManager中。我猜测如果是有xml配置的话,应该是postProcessBeforeInitialization最先读取xml配置中的filter的
// 这里先记录一下吧
getFilters().put(beanName, filter);
} else {
log.trace("Ignoring non-Filter bean ‘{}‘", beanName);
}
return bean;
}2、FactoryBean的接口实现
FactoryBean说白了就是一个工厂类的bean可以生产我们自己定义的bean并进行一些装饰
可以在这里看一下BeanFactory和FactoryBean的区别https://blog.csdn.net/weixin_38361347/article/details/92852611
public Object getObject() throws Exception {
if (instance == null) {
instance = createInstance();
}
return instance;
}
public Class getObjectType() {
return SpringShiroFilter.class;
}
显然我们的ShiroFilterFactoryBean创建的是SpringShiroFilter的实例
3、ShiroFilterFactoryBean.createInstance 方法
/**
* This implementation:
* <ol>
* <li>Ensures the required {@link #setSecurityManager(org.apache.shiro.mgt.SecurityManager) securityManager}
* property has been set</li>
* <li>{@link #createFilterChainManager() Creates} a {@link FilterChainManager} instance that reflects the
* configured {@link #setFilters(java.util.Map) filters} and
* {@link #setFilterChainDefinitionMap(java.util.Map) filter chain definitions}</li>
* <li>Wraps the FilterChainManager with a suitable
* {@link org.apache.shiro.web.filter.mgt.FilterChainResolver FilterChainResolver} since the Shiro Filter
* implementations do not know of {@code FilterChainManager}s</li>
* <li>Sets both the {@code SecurityManager} and {@code FilterChainResolver} instances on a new Shiro Filter
* instance and returns that filter instance.</li>
* </ol>
*
* @return a new Shiro Filter reflecting any configured filters and filter chain definitions.
* @throws Exception if there is a problem creating the AbstractShiroFilter instance.
*/
protected AbstractShiroFilter createInstance() throws Exception {
?
log.debug("Creating Shiro Filter instance.");
?
SecurityManager securityManager = getSecurityManager();
// 创建实例之前必须设置securityManager
if (securityManager == null) {
String msg = "SecurityManager property must be set.";
throw new BeanInitializationException(msg);
}
?
if (!(securityManager instanceof WebSecurityManager)) {
String msg = "The security manager does not implement the WebSecurityManager interface.";
throw new BeanInitializationException(msg);
}
//看下面分析4
FilterChainManager manager = createFilterChainManager();
?
//Expose the constructed FilterChainManager by first wrapping it in a
//FilterChainResolver implementation. The AbstractShiroFilter implementations
//do not know about FilterChainManagers - only resolvers:
//写死为PathMatchingFilterChainResolver
PathMatchingFilterChainResolver chainResolver = new PathMatchingFilterChainResolver();
chainResolver.setFilterChainManager(manager);
?
//Now create a concrete ShiroFilter instance and apply the acquired SecurityManager and built
//FilterChainResolver. It doesn‘t matter that the instance is an anonymous inner class
//here - we‘re just using it because it is a concrete AbstractShiroFilter instance that accepts
//injection of the SecurityManager and FilterChainResolver:
//构造SpringShiroFilter实例,由Spring管理
return new SpringShiroFilter((WebSecurityManager) securityManager, chainResolver);
}翻译一下上面的注释
实现创建这个实例之前:
1、必须确保SecurityManager属性已经设置了
2、创建一个FilterChainManager实例,该实例反映了我们配置的相关过滤内容filterChainDefinitionMap
3、用合适的方法包装FilterChainManager
4、因为shiro filter 实例不能感知到FilterChainManager,我们需要给它配置解析器FilterChainResolver
5、一个新的Shiro过滤器,它反映了所有已配置的过滤器和过滤器链定义。
4、ShiroFilterFactoryBean.createFilterChainManager 方法
看看PathMatchingFilterChainResolver和FilterChainManager的创建过程:
protected FilterChainManager createFilterChainManager() {
// 这个构造函数中会将shiro默认的Filter添加到FilterChainManager中.
// 点进去查看shiro的默认filter有哪些,见后面分析5
DefaultFilterChainManager manager = new DefaultFilterChainManager();
Map<String, Filter> defaultFilters = manager.getFilters();
// 将ShiroFilterFactoryBean配置的一些公共属性(上面配置的loginUrl, successUrl, unauthorizeUrl)应用到默认注册的filter上去
for (Filter filter : defaultFilters.values()) {
applyGlobalPropertiesIfNecessary(filter);
}
//Apply the acquired and/or configured filters:
// 然后再将用户配置的Filter并入; 所以如果用户配置了与上面同名的Filter, 则会进行覆盖操作
// 此处的getFilters();获取的是当前实例的filters属性,这个是属性值在创建默认的DefaultFilterChainManager的时候add进去的默认filter,以及在web.xml中自定义的filter(由postProcessBeforeInitialization注入)
Map<String, Filter> filters = getFilters();
if (!CollectionUtils.isEmpty(filters)) {
for (Map.Entry<String, Filter> entry : filters.entrySet()) {
String name = entry.getKey();
Filter filter = entry.getValue();
applyGlobalPropertiesIfNecessary(filter);
if (filter instanceof Nameable) {
((Nameable) filter).setName(name);
}
//‘init‘ argument is false, since Spring-configured filters should be initialized
// in Spring (i.e. ‘init-method=blah‘) or implement InitializingBean:
// spring会处理初始化问题, 所以shiro就不负责初始化了
manager.addFilter(name, filter, false);
}
}
//build up the chains:
// 这里将处理用户配置的ShiroFilterFactoryBean.filterChainDefinitions属性
Map<String, String> chains = getFilterChainDefinitionMap();
if (!CollectionUtils.isEmpty(chains)) {
for (Map.Entry<String, String> entry : chains.entrySet()) {
String url = entry.getKey();
String chainDefinition = entry.getValue();
// 见下面分析7
manager.createChain(url, chainDefinition);
}
}
return manager;
}5、shiro的DefaultFilterChainManager()
有必要来看看DefaultFilterChainManager的源码,分析一下上面调用到的方法。先来看看他的几个重要的属性:
其中filterConfig仅在初始化Filter时有效,而我们自定义的Filter都不是init的,所以该属性可以暂时忽略()。 而后面两张map就重要了:filters中缓存了所有添加的filter,filterChains则缓存了所有的filterChain。其中前者的key是filter name,value是Filter。而后者的key是chain name,value是NamedFilterList。 有的童鞋可能会问NamedFilterList是怎么样的结构呢,你可以把它当成List<Filter>,这样就好理解了
private FilterConfig filterConfig;
private Map<String, Filter> filters; //pool of filters available for creating chains
private Map<String, NamedFilterList> filterChains; //key: chain name, value: chain
DefaultFilterChainManager manager = new DefaultFilterChainManager();
// DefaultFilterChainManager构造函数
public DefaultFilterChainManager() {
this.addDefaultFilters(false);
}
?
protected void addDefaultFilters(boolean init) {
// shiro的DefaultFilter是一个枚举类,包含下面的11种启动时默认加载的filter
DefaultFilter[] var2 = DefaultFilter.values();
int var3 = var2.length;
// 遍历
for(int var4 = 0; var4 < var3; ++var4) {
DefaultFilter defaultFilter = var2[var4];
// 把filter添加到manager中,不需要初始化,由Spring来完成
this.addFilter(defaultFilter.name(), defaultFilter.newInstance(), init, false);
}
}
?
public enum DefaultFilter {
anon(AnonymousFilter.class),
authc(FormAuthenticationFilter.class),
authcBasic(BasicHttpAuthenticationFilter.class),
logout(LogoutFilter.class),
noSessionCreation(NoSessionCreationFilter.class),
perms(PermissionsAuthorizationFilter.class),
port(PortFilter.class),
rest(HttpMethodPermissionFilter.class),
roles(RolesAuthorizationFilter.class),
ssl(SslFilter.class),
user(UserFilter.class);
}6、filterChainDefinitionMap
在分析createChain前先 shiro是如何处理上面spring-shiro.xml中用户配置的 ShiroFilterFactoryBean.filterChainDefinitions属性的
下面的是使用xml配置时shiro读取配置文件的实现
//---------- ShiroFilterFactoryBean.setFilterChainDefinitions方法
public void setFilterChainDefinitions(String definitions) {
// 使用Ini类来解析用户配置的信息
Ini ini = new Ini();
ini.load(definitions);
//did they explicitly state a ‘urls‘ section? Not necessary, but just in case:
Ini.Section section = ini.getSection(IniFilterChainResolverFactory.URLS);
if (CollectionUtils.isEmpty(section)) {
//no urls section. Since this _is_ a urls chain definition property, just assume the
//default section contains only the definitions:
section = ini.getSection(Ini.DEFAULT_SECTION_NAME);
}
// 将解析出来的结果赋值给ShiroFilterFactoryBean的filterChainDefinitionMap字段
setFilterChainDefinitionMap(section);
}?
还有另外一种方式,在代码中使用注解的配置方式,直接用代码给filterChainDefinitionMap赋值即可,即本文最开头的配置代码中的方式
// 将拦截器链设置到shiro中 shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
7、DefaultFilterChainManager.createChain方法
这个方法执行完之后,用户配置的url权限校验(即ShiroFilterFactoryBean的filterChainDefinitions参数)就算是解析到位了(存放到了DefaultFilterChainManager的filterChains参数中)。
public void createChain(String chainName, String chainDefinition) {
// 以上面配置的filterChainDefinitions参数举例:
// 参数chainName形如 /admin/list**
// 参数chainDefinition形如 authc,perms[admin:manage]
if (!StringUtils.hasText(chainName)) {
throw new NullPointerException("chainName cannot be null or empty.");
}
if (!StringUtils.hasText(chainDefinition)) {
throw new NullPointerException("chainDefinition cannot be null or empty.");
}
if (log.isDebugEnabled()) {
log.debug("Creating chain [" + chainName + "] from String definition [" + chainDefinition + "]");
}
?
//parse the value by tokenizing it to get the resulting filter-specific config entries
//
//e.g. for a value of
//
// "authc, roles[admin,user], perms[file:edit]"
//
// the resulting token array would equal
//
// { "authc", "roles[admin,user]", "perms[file:edit]" }
//以上我们就可以看出我们所配置的ShiroFilterFactoryBean的filterChainDefinitions里的 每一行 会在这里被完整解析
String[] filterTokens = splitChainDefinition(chainDefinition);
?
//each token is specific to each filter.
//strip the name and extract any filter-specific config between brackets [ ]
for (String token : filterTokens) {
/* toNameConfigPair的解析结果参见下面这个,摘选自官方
Input Result
foo returned[0] == foo
returned[1] == null
foo[bar, baz] returned[0] == foo
returned[1] == bar, baz
*/
String[] nameConfigPair = toNameConfigPair(token);
//now we have the filter name, path and (possibly null) path-specific config. Let‘s apply them:
// 见分析8
addToChain(chainName, nameConfigPair[0], nameConfigPair[1]);
}
}8、DefaultFilterChainManager.addToChain方法
public void addToChain(String chainName, String filterName, String chainSpecificFilterConfig) {
if (!StringUtils.hasText(chainName)) {
throw new IllegalArgumentException("chainName cannot be null or empty.");
}
// 如果用户没有配置该filter, 则直接抛出的异常
Filter filter = getFilter(filterName);
if (filter == null) {
throw new IllegalArgumentException("There is no filter with name ‘" + filterName +
"‘ to apply to chain [" + chainName + "] in the pool of available Filters. Ensure a " +
"filter with that name/path has first been registered with the addFilter method(s).");
}
// 保存用户配置的url与filter之间的映射关系,注册到filter中
applyChainConfig(chainName, filter, chainSpecificFilterConfig);
// chainName为配置的url路径
// 这里会以用户配置的url路径来创建一个SimpleNamedFilterList示例; 并添加到DefaultFilterChainManager内部的Map<String, NamedFilterList>类型的类级字段filterChains中(以用户配置的url路径为key——即filterChainDefinitions参数里每一行等号左边的部分)
NamedFilterList chain = ensureChain(chainName);
chain.add(filter);
}前面八小节主要分析了shiroFilterFactoryBean创建一个实例的流程,主要包含创建FilterChainManager,filter的
装载到链的过程,后面部分主要是讲filter生效的过程源码。
至此,FilterChainManager就创建完了,它无非就是缓存了两张map,没有什么逻辑上的操作。下面将FilterChainManager设置到PathMatchingFilterChainResolver中。PathMatchingFilterChainResolver实现了FilterChainResolver接口,该接口中只定义了一个方法:
FilterChain getChain(ServletRequest request, ServletResponse response, FilterChain originalChain);
通过解析请求来得到一个新的FilterChain。而PathMatchingFilterChainResolver实现了该接口,依靠了FilterChainManager中保存的chainFilters和filters这两张map来根据请求路径解析出相应的filterChain,并且和originalChain组合起来使用。下面具体看看PathMatchingFilterChainResolver中的实现:
public FilterChain getChain(ServletRequest request, ServletResponse response, FilterChain originalChain) {
// 得到 FilterChainManager
FilterChainManager filterChainManager = getFilterChainManager();
if (!filterChainManager.hasChains()) {
return null;
}
?
String requestURI = getPathWithinApplication(request);
?
// chainNames就是刚定义的filterChains的keySet,也就是所有的路径集合(比如:["/resources/**","/login"])
for (String pathPattern : filterChainManager.getChainNames()) {
?
// 请求路径是否匹配某个 定义好的路径:
if (pathMatches(pathPattern, requestURI)) {
if (log.isTraceEnabled()) {
log.trace("Matched path pattern [" + pathPattern + "] for requestURI [" + requestURI + "]. " + "Utilizing corresponding filter chain...");
}
// 找到第一个匹配的Filter链,那么就返回一个ProxiedFilterChain
return filterChainManager.proxy(originalChain, pathPattern);
}
}
?
return null;
}这里返回只有两种情况,要么是null,要么就是一个ProxiedFilterChain。返回null并不表示中断FilterChain,而是只用originChain。而关于ProxiedFilterChain,它实现了FilterChain,内部维护了两份FilterChain(其实一个是FilterChain,另一个是List<Filter>) FilterChain也就是web.xml中注册的Filter形成的FilterChain,我们称之为originChain。而另一个List<Filter>则是我们在Shiro中注册的Filter链了,下面看看ProxiedFilterChain中关于doFilter(...)的实现:
public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException {
if (this.filters == null || this.filters.size() == this.index) {
//we‘ve reached the end of the wrapped chain, so invoke the original one:
if (log.isTraceEnabled()) {
log.trace("Invoking original filter chain.");
}
this.orig.doFilter(request, response);
} else {
if (log.isTraceEnabled()) {
log.trace("Invoking wrapped filter at index [" + this.index + "]");
}
this.filters.get(this.index++).doFilter(request, response, this);
}
}可以看到,它会先执行Shiro中执行的filter,然后再执行web.xml中的Filter。不过要注意的是,需要等到originChain执行到ShiroFilter之后才会执行Shiro中的Filter链。 至此,两个组件的创建过程差不多都介绍完了,那么当这两个组件创建完毕后,是如何工作的呢? 先从ShiroFilter入手,因为它是总的拦截器,看看其中的doFilterInternal(...)方法:
protected void doFilterInternal(ServletRequest servletRequest, ServletResponse servletResponse, final FilterChain chain)
throws ServletException, IOException {
?
Throwable t = null;
?
try {
final ServletRequest request = prepareServletRequest(servletRequest, servletResponse, chain);
final ServletResponse response = prepareServletResponse(request, servletResponse, chain);
?
final Subject subject = createSubject(request, response);
?
//noinspection unchecked
subject.execute(new Callable() {
public Object call() throws Exception {
// 其实需要关心的就在这里
// touch一下session
updateSessionLastAccessTime(request, response);
// 执行Filter链
executeChain(request, response, chain);
return null;
}
});
} catch (ExecutionException ex) {
t = ex.getCause();
} catch (Throwable throwable) {
t = throwable;
}
?
if (t != null) {
if (t instanceof ServletException) {
throw (ServletException) t;
}
if (t instanceof IOException) {
throw (IOException) t;
}
//otherwise it‘s not one of the two exceptions expected by the filter method signature - wrap it in one:
String msg = "Filtered request failed.";
throw new ServletException(msg, t);
}
}
跟进executeChain(...)方法:
protected void executeChain(ServletRequest request, ServletResponse response, FilterChain origChain)
throws IOException, ServletException {
FilterChain chain = getExecutionChain(request, response, origChain);
chain.doFilter(request, response);
}如何得到FilterChain的呢?如果你认真的看到这里,那么你应该不难想到其中肯定利用了刚才注册的ChainResolver:
protected FilterChain getExecutionChain(ServletRequest request, ServletResponse response, FilterChain origChain) {
FilterChain chain = origChain;
?
FilterChainResolver resolver = getFilterChainResolver();
if (resolver == null) {
log.debug("No FilterChainResolver configured. Returning original FilterChain.");
return origChain;
}
?
FilterChain resolved = resolver.getChain(request, response, origChain);
if (resolved != null) {
log.trace("Resolved a configured FilterChain for the current request.");
chain = resolved;
} else {
log.trace("No FilterChain configured for the current request. Using the default.");
}
?
return chain;
}猜对了~并且也验证了当resolver.getChain(...)返回null时,直接使用originChain了。然后执行返回的FilterChain的doFilter(...)方法。这个过程我们再脱离代码来分析一下:当我们从浏览器发出一个请求,究竟发生了什么? 这里只站在Filter的层面来分析。服务器启动后,读取web.xml中的filter、filter-mapping节点后组成FilterChain,对请求进行拦截。拦截的顺序按照filter节点的定义顺序,Shiro利用ShiroFilter来充当一个总的拦截器来分发所有需要被Shiro拦截的请求,所以我们看到在Shiro中我们还可以自定义拦截器。ShiroFilter根据它在拦截器中的位置,只要执行到了那么就会暂时中断原FilterChain的执行,先执行Shiro中定义的Filter,最后再执行原FilterChian。可以打个比方,比如说本来有一条铁链,一直蚂蚁从铁链的开端往末端爬,其中某一环叫ShiroFilter,那么当蚂蚁爬到ShiroFilter这一环时,将铁链打断,并且接上另一端铁链(Shiro中自定义的Filter),这样就构成了一条新的铁链。然后蚂蚁继续爬行(后续的执行过程)。
到这里,我们已经根据请求路径找到了一条Filter链(originChain + shiroChain),之后就是对链上的Filter做doFilter,其中关于如何 就是filter后配置的[]部分是如何生效的,我们可以看PathMatchingFilter中的Prehandle(...)方法:
protected boolean preHandle(ServletRequest request, ServletResponse response) throws Exception {
?
if (this.appliedPaths == null || this.appliedPaths.isEmpty()) {
if (log.isTraceEnabled()) {
log.trace("appliedPaths property is null or empty. This Filter will passthrough immediately.");
}
return true;
}
?
// appliedPaths中保存了该filter中能拦截的路径和该路径配置的key-value对,比如{key="/admin/**", value="[admin]"}
for (String path : this.appliedPaths.keySet()) {
// 首先是匹配路径
if (pathsMatch(path, request)) {
log.trace("Current requestURI matches pattern ‘{}‘. Determining filter chain execution...", path);
// 然后开始验证“[]”中的字符串
Object config = this.appliedPaths.get(path);
return isFilterChainContinued(request, response, path, config);
}
}
?
//no path matched, allow the request to go through:
return true;
}下面跟踪isFilterChainContinued(...):
private boolean isFilterChainContinued(ServletRequest request, ServletResponse response,
String path, Object pathConfig) throws Exception {
?
if (isEnabled(request, response, path, pathConfig)) { //isEnabled check added in 1.2
if (log.isTraceEnabled()) {
// log
}
return onPreHandle(request, response, pathConfig);
}
?
if (log.isTraceEnabled()) {
// log
}
return true;
}基本也就是交给onPreHandle(...)来处理,所以一般需要验证”[]“中字符串的filter都会扩展这个方法,比如AccessControlFilter:
public boolean onPreHandle(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception {
return isAccessAllowed(request, response, mappedValue) || onAccessDenied(request, response, mappedValue);
}
// 而RolesAuthorizationFilter中:
public boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) throws IOException {
?
Subject subject = getSubject(request, response);
String[] rolesArray = (String[]) mappedValue;
?
if (rolesArray == null || rolesArray.length == 0) {
//no roles specified, so nothing to check - allow access.
return true;
}
?
Set<String> roles = CollectionUtils.asSet(rolesArray);
return subject.hasAllRoles(roles);
}这里面就牵扯出一个有趣的问题 : FactoryBean<T>接口的getObject方法和 BeanPostProcessor接口的postProcessBeforeInitialization的执行先后顺序?
为了保证不遗漏Filter,我们可以猜测后者必须优先于前者。因为创建实例要读取初始化的默认filter
上面这个说法参考下面第一个博客的说法有一点要注意的是,打断点调试确实是postProcessBeforeInitialization先进去的但是并没有进去找到任何的filter,因为我是注解提供的filterMap,所以是在执行createFilterChainManager的时候先查找default的filter再添加我们在config类中传进去的flterMap解析filter到filterChainManager中。我猜测如果是有xml配置的话,应该是postProcessBeforeInitialization最先读取xml配置中的filter的,但是如果是xml配置应该是一开始在postProcessBeforeInitialization中是找不到的
在两位大佬的基础上进行了理解,代码的一步步跟踪,分析源代码的时候结合ide的debug功能打断点一行行的跳转,加深理解,再加上自己的注释,后面那位花了两天时间走完了,我也差不多,哈哈哈哈哈。。做个笔记,防止忘记。
参考文章:https://blog.csdn.net/lqzkcx3/article/details/78776555
https://blog.csdn.net/u012345283/article/details/44199791
