Shiro流程简析 过滤器

简介

本文简单分析Spring Shiro框架中过滤器的使用,涉及过滤器的配置、初始化过程、过滤流程;

配置

Shiro中对过滤器的配置,核心是对ShiroFilterFactoryBean的配置,主要分为三部分:

  1. 配置SecurityManager;
  2. 配置过滤器;
  3. 配置请求URL过滤规则;

常见的配置代码如下:

@Bean("shiroFilterFactoryBean")
public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) {
    ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();

    // 设置SecurityManager
    shiroFilterFactoryBean.setSecurityManager(securityManager);

    // 自定义过滤规则
    Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
    filterChainDefinitionMap.put("/**", "authc");
    shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);

    // 自定义过滤器
    LinkedHashMap<String, Filter> filterMap = new LinkedHashMap<>();
    filterMap.put("authc", new ShiroFormAuthenticationFilter());
    shiroFilterFactoryBean.setFilters(filterMap);

    return shiroFilterFactoryBean;
}

配置SecurityManager

ShiroFilterFactoryBean需要注入SecurityManager实例,且要求为WebSecurityManager类型;ShiroFilterFactoryBean在创建AbstractShiroFilter时,会先校验SecurityManager实例;

配置过滤器

可以自定义过滤器实现,并设置该过滤器实现的名称;设置的名称和Shiro框架默认提供的过滤器的名称:

  • 相同,则覆盖Shiro框架默认提供的过滤器;
  • 不同,则添加该过滤器到Shiro框架的过滤器集合;

DefaultFilter定义了Shiro框架默认提供的过滤器和名称的对应关系,如下:

anon(AnonymousFilter.class),
authc(FormAuthenticationFilter.class),
authcBasic(BasicHttpAuthenticationFilter.class),
authcBearer(BearerHttpAuthenticationFilter.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),
invalidRequest(InvalidRequestFilter.class);

配置请求URL过滤规则

配置请求URL需要被哪些过滤器处理,可以使用Shiro框架默认提供的过滤器,也可以使用自定义过滤器实现,前提是自定义过滤器实现已经加入到Shiro框架的过滤器集合中;

请求URL支持通配符;

初始化

本文主要分析Shiro框架中核心过滤器ShiroFilterFactoryBean的初始化过程,ShiroFilterFactoryBean是个BeanFactory,用于构建AbstractShiroFilter Bean,所以本质是初始化ShiroFilterFactoryBean.SpringShiroFilter;

入口为:ServletContextInitializerBeans的getOrderedBeansOfType(ListableBeanFactory beanFactory, Class<T> type, Set<?> excludes);type为interface javax.servlet.Filter;

private <T> List<Entry<String, T>> getOrderedBeansOfType(
			ListableBeanFactory beanFactory, Class<T> type, Set<?> excludes) {
		Comparator<Entry<String, T>> comparator = (o1,
				o2) -> AnnotationAwareOrderComparator.INSTANCE.compare(o1.getValue(),
						o2.getValue());
        // 获取javax.servlet.Filter类型beanName列表,其中包括"shiroFilterFactoryBean"
		String[] names = beanFactory.getBeanNamesForType(type, true, false);
		Map<String, T> map = new LinkedHashMap<>();
		for (String name : names) {
			if (!excludes.contains(name) && !ScopedProxyUtils.isScopedTarget(name)) {
                // 根据"shiroFilterFactoryBean"获取Filter Bean
				T bean = beanFactory.getBean(name, type);
				if (!excludes.contains(bean)) {
                    // 排除项中不包含,则添加到返回结果集合
					map.put(name, bean);
				}
			}
		}
		List<Entry<String, T>> beans = new ArrayList<>();
		beans.addAll(map.entrySet());
        // 对所有的Bean排序后返回
		beans.sort(comparator);
		return beans;
	}

主要有两个核心动作:

  1.         获取javax.servlet.Filter类型beanName列表,其中包括"shiroFilterFactoryBean";
  2.         根据beanName获取Filter Bean;

1. 获取javax.servlet.Filter类型beanName列表,getOrderedBeansOfType由DefaultListableBeanFactory实现,内部调用doGetBeanNamesForType;

private String[] doGetBeanNamesForType(ResolvableType type, boolean includeNonSingletons, boolean allowEagerInit) {
	List<String> result = new ArrayList<>();

    // 遍历DefaultListableBeanFactory的beanDefinitionNames集合
	// Check all bean definitions.
	for (String beanName : this.beanDefinitionNames) {
		// Only consider bean as eligible if the bean name
		// is not defined as alias for some other bean.
		if (!isAlias(beanName)) {
			try {
				RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
				// Only check bean definition if it is complete.
				if (!mbd.isAbstract() && (allowEagerInit ||
						(mbd.hasBeanClass() || !mbd.isLazyInit() || isAllowEagerClassLoading()) &&
								!requiresEagerInitForType(mbd.getFactoryBeanName()))) {
                    // 判断当前beanName对应的Bean是否是FactoryBean,"shiroFilterFactoryBean"是FactoryBean
					// In case of FactoryBean, match object created by FactoryBean.
					boolean isFactoryBean = isFactoryBean(beanName, mbd);
					BeanDefinitionHolder dbd = mbd.getDecoratedDefinition();
					boolean matchFound =
							(allowEagerInit || !isFactoryBean ||
									(dbd != null && !mbd.isLazyInit()) || containsSingleton(beanName)) &&
							(includeNonSingletons ||
									(dbd != null ? mbd.isSingleton() : isSingleton(beanName))) &&
                            // 进一步判断当前beanName对应的Bean是否匹配javax.servlet.Filter
							isTypeMatch(beanName, type);
					if (!matchFound && isFactoryBean) {
						// In case of FactoryBean, try to match FactoryBean instance itself next.
						beanName = FACTORY_BEAN_PREFIX + beanName;
						matchFound = (includeNonSingletons || mbd.isSingleton()) && isTypeMatch(beanName, type);
					}
					if (matchFound) {
                        // 当前beanName匹配javax.servlet.Filter,则加入匹配结果中
						result.add(beanName);
					}
				}
			}
			catch (CannotLoadBeanClassException ex) {
                // 此处代码省略
                ...
			}
		}
	}

    // 继续遍历DefaultListableBeanFactory的manualSingletonNames集合,添加匹配的beanName
	// Check manually registered singletons too.
	for (String beanName : this.manualSingletonNames) {
        // 基本同beanDefinitionNames
        // 此处代码省略
        ...
	}

	return StringUtils.toStringArray(result);
}

        1.1 遍历DefaultListableBeanFactory的beanDefinitionNames集合,针对beanName为"shiroFilterFactoryBean"情况;

        1.2 判断当前beanName是否是FactoryBean,满足;

        1.3 进一步判断当前beanName是否匹配javax.servlet.Filter,匹配;

public boolean isTypeMatch(String name, ResolvableType typeToMatch) throws NoSuchBeanDefinitionException {
	String beanName = transformedBeanName(name);

	// Check manually registered singletons.
	Object beanInstance = getSingleton(beanName, false);
	if (beanInstance != null && beanInstance.getClass() != NullBean.class) {
        // 如果是beanName对应的beanInstance是FactoryBean类型
		if (beanInstance instanceof FactoryBean) {
			if (!BeanFactoryUtils.isFactoryDereference(name)) {
                // 获取FactoryBean创建的Bean类型
				Class<?> type = getTypeForFactoryBean((FactoryBean<?>) beanInstance);
                // 判断Bean类型是否匹配javax.servlet.Filter
				return (type != null && typeToMatch.isAssignableFrom(type));
			}
			else {
				return typeToMatch.isInstance(beanInstance);
			}
		}
    }
    // 此处代码省略
    ...
}

                1.3.1 判断beanName对应的beanInstance是FactoryBean类型,满足;

                1.3.2 获取FactoryBean创建的Bean类型;

                        内部通过FactoryBean.getObjectType()获取本Bean工厂创建的Bean类型;

                1.3.3 判断Bean类型是否匹配javax.servlet.Filter,满足;

        1.4 当前beanName匹配javax.servlet.Filter,则加入匹配结果中;

        1.5 继续遍历DefaultListableBeanFactory的manualSingletonNames集合,添加匹配的beanName;

2. 根据beanName为"shiroFilterFactoryBean"获取Filter Bean,getBean由AbstractBeanFactory实现,内部调用doGetBean;

protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
			@Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {

	final String beanName = transformedBeanName(name);
	Object bean;

    // 根据beanName获取singleton Bean
	// Eagerly check singleton cache for manually registered singletons.
	Object sharedInstance = getSingleton(beanName);
	if (sharedInstance != null && args == null) {
		// log输出,代码省略

        // 获取BeanFactory创建的Bean
		bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
	}
    // 此处代码省略
    ...

    // 校验构建出来的Bean是否匹配指定的类型
    // Check if required type matches the type of the actual bean instance.
	if (requiredType != null && !requiredType.isInstance(bean)) {
        // 必要时转换Bean
        // 此处代码省略
        ...
    }
    // 校验通过则返回构建出来的Bean
	return (T) bean;
}

        2.1 根据beanName获取singleton Bean;

        2.2 获取BeanFactory创建的Bean;

               由AbstractAutowireCapableBeanFactory实现,内部调用父类AbstractBeanFactory的getObjectForBeanInstance实现;

protected Object getObjectForBeanInstance(
			Object beanInstance, String name, String beanName, @Nullable RootBeanDefinition mbd) {

    // 判断beanName是否是isFactoryDereference
	// Don't let calling code try to dereference the factory if the bean isn't a factory.
	if (BeanFactoryUtils.isFactoryDereference(name)) {
		if (beanInstance instanceof NullBean) {
			return beanInstance;
		}
		if (!(beanInstance instanceof FactoryBean)) {
			throw new BeanIsNotAFactoryException(transformedBeanName(name), beanInstance.getClass());
		}
	}

	// Now we have the bean instance, which may be a normal bean or a FactoryBean.
	// If it's a FactoryBean, we use it to create a bean instance, unless the
	// caller actually wants a reference to the factory.
	if (!(beanInstance instanceof FactoryBean) || BeanFactoryUtils.isFactoryDereference(name)) {
		return beanInstance;
	}

	Object object = null;
	if (mbd == null) {
        // 尝试从缓存中获取Bean
		object = getCachedObjectForFactoryBean(beanName);
	}
	if (object == null) {
		// Return bean instance from factory.
		FactoryBean<?> factory = (FactoryBean<?>) beanInstance;
		// Caches object obtained from FactoryBean if it is a singleton.
		if (mbd == null && containsBeanDefinition(beanName)) {
			mbd = getMergedLocalBeanDefinition(beanName);
		}
		boolean synthetic = (mbd != null && mbd.isSynthetic());
        // 从FactoryBean中获取Bean
		object = getObjectFromFactoryBean(factory, beanName, !synthetic);
	}
	return object;
}

                2.2.1 判断beanName是否是isFactoryDereference,不满足;

                2.2.2 尝试从缓存中获取Bean,获取不到;

                2.2.3 从FactoryBean中获取Bean;

protected Object getObjectFromFactoryBean(FactoryBean<?> factory, String beanName, boolean shouldPostProcess) {
    // 判断Bean是否是singleton
	if (factory.isSingleton() && containsSingleton(beanName)) {
		synchronized (getSingletonMutex()) {
            // 尝试从Bean工厂缓存中获取singleton Bean
			Object object = this.factoryBeanObjectCache.get(beanName);
			if (object == null) {
                // 从FactoryBean中获取Bean
				object = doGetObjectFromFactoryBean(factory, beanName);
				// Only post-process and store if not put there already during getObject() call above
				// (e.g. because of circular reference processing triggered by custom getBean calls)
				Object alreadyThere = this.factoryBeanObjectCache.get(beanName);
				if (alreadyThere != null) {
					object = alreadyThere;
				}
				else {
					if (shouldPostProcess) {
						if (isSingletonCurrentlyInCreation(beanName)) {
							// Temporarily return non-post-processed object, not storing it yet..
							return object;
						}
                        // singleton构建前处理,默认实现为将正在构建的beanName添加到构建集合中
						beforeSingletonCreation(beanName);
						try {
                            // singleton构建后置处理,即应用BeanPostProcessors到构建的Bean
							object = postProcessObjectFromFactoryBean(object, beanName);
						}
						catch (Throwable ex) {
							throw new BeanCreationException(beanName,
									"Post-processing of FactoryBean's singleton object failed", ex);
						}
						finally {
                            // singleton构建后的处理,默认实现为将构建完成的singleton从构建集合中移除
							afterSingletonCreation(beanName);
						}
					}
					if (containsSingleton(beanName)) {
                        // 将构建完成的singleton放入Bean工厂缓存
						this.factoryBeanObjectCache.put(beanName, object);
					}
				}
			}
			return object;
		}
	}
	// 此处代码省略
    ...
}

                        2.2.3.1 判断BeanFactory是否是singleton且包含该beanName,满足;

                        2.2.3.2 尝试从Bean工厂缓存中获取singleton Bean;

                        2.2.3.3 从FactoryBean中获取Bean;

                            最终调用factory.getObject()方法获取Bean,ShiroFilterFactoryBean内部调用createInstance创建AbstractShiroFilter实例;

protected AbstractShiroFilter createInstance() throws Exception {

    log.debug("Creating Shiro Filter instance.");

    // 校验SecurityManager的有效性
    SecurityManager securityManager = getSecurityManager();
    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);
    }

    // 创建过滤器链管理器
    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 chainResolver = new PathMatchingFilterChainResolver();
    chainResolver.setFilterChainManager(manager);

    // 创建SpringShiroFilter实例并返回
    //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:
    return new SpringShiroFilter((WebSecurityManager) securityManager, chainResolver);
    }

                                2.2.3.3.1 校验SecurityManager的有效性;

                                2.2.3.3.2 创建过滤器链管理器;

protected FilterChainManager createFilterChainManager() {

    // 默认创建DefaultFilterChainManager,并应用全局配置到默认过滤器
    DefaultFilterChainManager manager = new DefaultFilterChainManager();
    Map<String, Filter> defaultFilters = manager.getFilters();

    // 应用全局配置到默认过滤器
    //apply global settings if necessary:
    for (Filter filter : defaultFilters.values()) {
        applyGlobalPropertiesIfNecessary(filter);
    }

    // 处理配置的过滤器
    //Apply the acquired and/or configured filters:
    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:
            // 并将配置的过滤器添加到默认过滤器中,同名则覆盖默认过滤器
            manager.addFilter(name, filter, false);
        }
    }

    // 设置全局过滤器
    // set the global filters
    manager.setGlobalFilters(this.globalFilters);

    // 根据配置构建url的过滤器链
    //build up the chains:
    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();
            manager.createChain(url, chainDefinition);
        }
    }

    // 构建默认过滤器链,匹配任意未匹配的路径
    // create the default chain, to match anything the path matching would have missed
    manager.createDefaultChain("/**"); // TODO this assumes ANT path matching, which might be OK here

    return manager;
}

                                        2.2.3.3.2.1 默认创建DefaultFilterChainManager,并应用全局配置到默认过滤器;

                                        2.2.3.3.2.2 应用全局配置到配置的过滤器,并将过滤器添加到默认过滤器中,同名则覆盖默认过滤器;

                                        2.2.3.3.2.3 设置全局过滤器;

                                        2.2.3.3.2.4 根据配置构建url的过滤器链;

                                        2.2.3.3.2.5 构建默认过滤器链,匹配任意未匹配的路径;

                                2.2.3.3.3 配置路径匹配过滤器链解析器;

                                2.2.3.3.4 创建SpringShiroFilter实例并返回;

                        2.2.3.4 singleton构建前处理,默认实现为将正在构建的beanName添加到构建集合中;

                        2.2.3.5 singleton构建后置处理,即应用BeanPostProcessors到构建的Bean;

                        2.2.3.6 singleton构建后的处理,默认实现为将构建完成的singleton从构建集合中移除;

                        2.2.3.7 将构建完成的singleton放入Bean工厂缓存;

        2.3 校验构建出来的Bean是否匹配指定的类型;

        2.4 校验通过则返回构建出来的Bean;

3. 排除项中不包含构建的Bean,则添加到返回结果集合中;

4. 对所有的Bean排序后返回;

过滤

Spring Shiro创建了ShiroFilterFactoryBean Bean,则在处理请求的过滤器链中会存在一个过滤器AbstractShiroFilter,AbstractShiroFilter在执行过滤逻辑时,会将原过滤器链进行代理,在代理过滤器链ProxiedFilterChain中,优先将Shiro配置的过滤器链执行完,再执行原过滤器链,从而在请求被真正处理之前对请求进行权限管理相关操作;

对每一个请求,都会经过:创建过滤器链、执行过滤器链、释放过滤器链;

private void invoke(ServletRequest request, ServletResponse response,
            State state) throws IOException, ServletException {

    // 创建过滤器链
    // Get the FilterChain Here
    ApplicationFilterChain filterChain =
        ApplicationFilterFactory.createFilterChain(request, wrapper, servlet);

    // 调用过滤器链
    // Call the service() method for the allocated servlet instance
    try {
        // for includes/forwards
        if ((servlet != null) && (filterChain != null)) {
            filterChain.doFilter(request, response);
        }
        // Servlet Service Method is called by the FilterChain
    }
    // 异常处理
    // 此处代码省略
    ...

    // 释放过滤器链
    // Release the filter chain (if any) for this request
    try {
        if (filterChain != null)
        filterChain.release();
    }
    // 异常处理
    // 此处代码省略
    ...
}

1. 为当前请求创建过滤器链ApplicationFilterChain;

public static ApplicationFilterChain createFilterChain(ServletRequest request,
        Wrapper wrapper, Servlet servlet) {

    // If there is no servlet to execute, return null
    if (servlet == null)
        return null;

    // 创建并初始化ApplicationFilterChain
    // Create and initialize a filter chain object
    ApplicationFilterChain filterChain = null;
    // 此处代码省略
    ...

    // 从上下文StandardContext中获取过滤器映射集合
    // Acquire the filter mappings for this Context
    StandardContext context = (StandardContext) wrapper.getParent();
    FilterMap filterMaps[] = context.findFilterMaps();

    // 如果过滤器映射集合为空,则直接返回ApplicationFilterChain
    // If there are no filter mappings, we are done
    if ((filterMaps == null) || (filterMaps.length == 0))
        return (filterChain);

    // Acquire the information we will need to match filter mappings
    DispatcherType dispatcher =
        (DispatcherType) request.getAttribute(Globals.DISPATCHER_TYPE_ATTR);

    String requestPath = null;
    Object attribute = request.getAttribute(Globals.DISPATCHER_REQUEST_PATH_ATTR);
    if (attribute != null){
        requestPath = attribute.toString();
    }

    String servletName = wrapper.getName();

    // 遍历过滤器映射集合,将路径匹配的过滤器加入到处理该请求的过滤器链
    // Add the relevant path-mapped filters to this filter chain
    for (int i = 0; i < filterMaps.length; i++) {
        // 是否匹配dispatcher
        if (!matchDispatcher(filterMaps[i] ,dispatcher)) {
            continue;
        }
        // 是否匹配请求路径
        if (!matchFiltersURL(filterMaps[i], requestPath))
            continue;
        ApplicationFilterConfig filterConfig = (ApplicationFilterConfig)
            context.findFilterConfig(filterMaps[i].getFilterName());
        if (filterConfig == null) {
            // FIXME - log configuration problem
            continue;
        }
        // 添加过滤器
        filterChain.addFilter(filterConfig);
    }

    // 遍历过滤器映射集合,将Servlet名称匹配的过滤器加入到处理该请求的过滤器链
    // Add filters that match on servlet name second
    for (int i = 0; i < filterMaps.length; i++) {
        // 是否匹配dispatcher
        if (!matchDispatcher(filterMaps[i] ,dispatcher)) {
            continue;
        }
        // 是否匹配Servlet名称
        if (!matchFiltersServlet(filterMaps[i], servletName))
            continue;
        ApplicationFilterConfig filterConfig = (ApplicationFilterConfig)
            context.findFilterConfig(filterMaps[i].getFilterName());
        if (filterConfig == null) {
            // FIXME - log configuration problem
            continue;
        }
        // 添加过滤器
        filterChain.addFilter(filterConfig);
    }

    // 返回过滤器链
    // Return the completed filter chain
    return filterChain;
}

        1.1 从上下文StandardContext从获取过滤器映射集合;

            过滤器映射集合包含的过滤器如下:

                1. characterEncodingFilter -> OrderedCharacterEncodingFilter

                2. hiddenHttpMethodFilter -> OrderedHiddenHttpMethodFilter

                3. httpPutFormContentFilter -> OrderedHttpPutFormContentFilter

                4. requestContextFilter -> OrderedRequestContextFilter

                5. filterName -> StatViewFilter

                6. shiroFilterFactoryBean -> ShiroFilterFactoryBean$SpringShiroFilter(Shiro)

                7. filterName -> WsFilter

            匹配的路径都是 /*,即全匹配;

        1.2 遍历过滤器映射集合,将路径匹配的过滤器加入到处理该请求的过滤器链;

void addFilter(ApplicationFilterConfig filterConfig) {

    // 遍历当前已存在的过滤器列表
    // Prevent the same filter being added multiple times
    for(ApplicationFilterConfig filter:filters)
        if(filter==filterConfig)
            // 若已存在,则无需添加,直接返回
            return;

    // 校验过滤器列表长度,如果无空间存储,则扩容(容量增10)
    if (n == filters.length) {
        ApplicationFilterConfig[] newFilters =
            new ApplicationFilterConfig[n + INCREMENT];
        System.arraycopy(filters, 0, newFilters, 0, n);
        filters = newFilters;
    }
    // 添加到过滤器列表
    filters[n++] = filterConfig;
}

                1.2.1 遍历当前已存在的过滤器列表,若已存在,则无需添加,直接返回;                  

                1.2.2 校验过滤器列表长度,如果无空间存储,则扩容(容量增10);

                1.2.3 添加到过滤器列表;

        1.3 遍历过滤器映射集合,将Servlet名称匹配的过滤器加入到处理该请求的过滤器链;

            添加过滤器逻辑同1.2;

        1.4 返回过滤器链;

2. 执行过滤器链;

    过滤器链成功执行完后会调用servlet.service(request, response);

    非安全模式直接调用internalDoFilter(request, response);

private void internalDoFilter(ServletRequest request, ServletResponse response)
        throws IOException, ServletException {

    // 执行过滤器列表中下一个过滤器的过滤逻辑;
    // 过滤器执行完过滤逻辑后,
    //     要么调用filterChain.doFilter方法,继续下一个过滤器的处理;
    //     要么设置Response,直接返回;
    //     要么抛出异常,由StandardWrapperValve设置Response;
    // Call the next filter if there is one
    if (pos < n) {
        ApplicationFilterConfig filterConfig = filters[pos++];
        try {
            // 获取过滤器
            Filter filter = filterConfig.getFilter();

            if (request.isAsyncSupported() && "false".equalsIgnoreCase(
                    filterConfig.getFilterDef().getAsyncSupported())) {
                request.setAttribute(Globals.ASYNC_SUPPORTED_ATTR, Boolean.FALSE);
            }
            if( Globals.IS_SECURITY_ENABLED ) {
                final ServletRequest req = request;
                final ServletResponse res = response;
                Principal principal =
                    ((HttpServletRequest) req).getUserPrincipal();

                Object[] args = new Object[]{req, res, this};
                SecurityUtil.doAsPrivilege ("doFilter", filter, classType, args, principal);
            } else {
                // 执行过滤器
                filter.doFilter(request, response, this);
            }
        } catch (IOException | ServletException | RuntimeException e) {
            throw e;
        } catch (Throwable e) {
            e = ExceptionUtils.unwrapInvocationTargetException(e);
            ExceptionUtils.handleThrowable(e);
            throw new ServletException(sm.getString("filterChain.filter"), e);
        }
        return;
    }

    // 成功遍历完过滤器列表,则处理请求
    // We fell off the end of the chain -- call the servlet instance
    try {
        if (ApplicationDispatcher.WRAP_SAME_OBJECT) {
            lastServicedRequest.set(request);
            lastServicedResponse.set(response);
        }

        if (request.isAsyncSupported() && !servletSupportsAsync) {
            request.setAttribute(Globals.ASYNC_SUPPORTED_ATTR, Boolean.FALSE);
        }
        // Use potentially wrapped request from this point
        if ((request instanceof HttpServletRequest) &&
                (response instanceof HttpServletResponse) &&
                Globals.IS_SECURITY_ENABLED ) {
            final ServletRequest req = request;
            final ServletResponse res = response;
            Principal principal =
                ((HttpServletRequest) req).getUserPrincipal();
            Object[] args = new Object[]{req, res};
            SecurityUtil.doAsPrivilege("service",
                                       servlet,
                                       classTypeUsedInService,
                                       args,
                                       principal);
        } else {
            servlet.service(request, response);
        }
    } catch (IOException | ServletException | RuntimeException e) {
        throw e;
    } catch (Throwable e) {
        e = ExceptionUtils.unwrapInvocationTargetException(e);
        ExceptionUtils.handleThrowable(e);
        throw new ServletException(sm.getString("filterChain.servlet"), e);
    } finally {
        if (ApplicationDispatcher.WRAP_SAME_OBJECT) {
            lastServicedRequest.set(null);
            lastServicedResponse.set(null);
        }
    }
}

        2.1 执行过滤器列表中下一个过滤器的过滤逻辑;

            过滤器执行完过滤逻辑后,

                要么调用filterChain.doFilter方法,继续下一个过滤器的处理;

                要么设置Response,直接返回;

                要么抛出异常,由StandardWrapperValve设置Response;

            本文只关注ShiroFilterFactoryBean$SpringShiroFilter的过滤逻辑;

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);

        // 创建Subject
        final Subject subject = createSubject(request, response);

        // 创建任务线程执行过滤器链
        //noinspection unchecked
        subject.execute(new Callable() {
            public Object call() throws Exception {
                updateSessionLastAccessTime(request, response);
                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);
    }
}

                2.1.1 创建Subject

                2.1.2 创建任务线程执行过滤器链;

protected void executeChain(ServletRequest request, ServletResponse response, FilterChain origChain) throws IOException, ServletException {
    // 构造该请求的代理过滤器链
    FilterChain chain = getExecutionChain(request, response, origChain);
    // 执行过滤器链
    chain.doFilter(request, response);
}

                        2.1.2.1 构造该请求的代理过滤器链;

                            内部由PathMatchingFilterChainResolver.getChain构造代理过滤器链,若无需代理,则返回原过滤器链;

public FilterChain getChain(ServletRequest request, ServletResponse response, FilterChain originalChain) {
    FilterChainManager filterChainManager = getFilterChainManager();
    if (!filterChainManager.hasChains()) {
        // 若FilterChainManager无配置的过滤器,则无需代理,直接返回null
        return null;
    }

    final String requestURI = getPathWithinApplication(request);
    final String requestURINoTrailingSlash = removeTrailingSlash(requestURI);

    // 遍历FilterChainManager中配置的过滤器链集合
    //the 'chain names' in this implementation are actually path patterns defined by the user.  We just use them
    //as the chain name for the FilterChainManager's requirements
    for (String pathPattern : filterChainManager.getChainNames()) {
        // If the path does match, then pass on to the subclass implementation for specific checks:
        // 判断Request的路径匹配是否已配置的过滤器链的路径
        // 借助PatternMatcher判断两个路径是否匹配
        if (pathMatches(pathPattern, requestURI)) {
            if (log.isTraceEnabled()) {
                log.trace("Matched path pattern [{}] for requestURI [{}].  " +
                        "Utilizing corresponding filter chain...", pathPattern, Encode.forHtml(requestURI));
            }
            // 匹配成功,则代理原过滤器链
            return filterChainManager.proxy(originalChain, pathPattern);
        } else {

            // in spring web, the requestURI "/resource/menus" ---- "resource/menus/" bose can access the resource
            // but the pathPattern match "/resource/menus" can not match "resource/menus/"
            // user can use requestURI + "/" to simply bypassed chain filter, to bypassed shiro protect
            // 去除尾部路径符,再匹配一次
            pathPattern = removeTrailingSlash(pathPattern);

            if (pathMatches(pathPattern, requestURINoTrailingSlash)) {
                if (log.isTraceEnabled()) {
                    log.trace("Matched path pattern [{}] for requestURI [{}].  " +
                              "Utilizing corresponding filter chain...", pathPattern, Encode.forHtml(requestURINoTrailingSlash));
                }
                // 匹配成功,则代理原过滤器链
                return filterChainManager.proxy(originalChain, requestURINoTrailingSlash);
            }
        }
    }

    // 无需代理,返回null
    return null;
}

                                2.1.2.1.1 若FilterChainManager无配置的过滤器,则无需代理,直接返回null;

                                2.1.2.1.2 遍历FilterChainManager中配置的过滤器链集合;

                                2.1.2.1.3 判断Request的路径匹配是否已配置的过滤器链的路径;

                                    借助PatternMatcher判断两个路径是否匹配,Shiro框架中存在默认过滤器链("/**"),会匹配任意URL;

                                2.1.2.1.4 若匹配成功,则代理原过滤器链;                                  

                                    代理的过滤器链为ProxiedFilterChain;

                        2.1.2.2 执行过滤器链;

                            若存在代理,则为ProxiedFilterChain.doFilter(request, response);

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);
    }
}

                                2.1.2.2.1 如果过滤器集合为空,或者已全部执行完,则执行原过滤器链;

                                2.1.2.2.2 执行过滤器集合中下一个过滤器的过滤逻辑;

                                    以FormAuthenticationFilter举例;

                                        2.1.2.2.2.1 调用OncePerRequestFilter.doFilter算法模板,保证只执行一次过滤逻辑;

                                        2.1.2.2.2.2 调用AdviceFilter.doFilterInternal算法模板,进行preHandle/executeChain(如果preHandle返回false则不调用)/postHandle/cleanup处理;

                                        2.1.2.2.2.3 调用PathMatchingFilter.preHandle算法模板,如果需要拦截请求,则进行isFilterChainContinued处理;

                                        2.1.2.2.2.4 调用AccessControlFilter.onPreHandle算法模板,进行isAccessAllowed/onAccessDenied处理;

                                        2.1.2.2.2.5 调用AuthenticatingFilter.isAccessAllowed方法,内部会调用父类AuthenticationFilter.isAccessAllowed方法,判断当前用户是否已登录;

                                        2.1.2.2.2.6 调用FormAuthenticationFilter.onAccessDenied方法,进行登录尝试,或者跳转到登录页面;

        2.2 成功遍历完过滤器列表,则处理请求;

servlet.service(request, response);

3. 释放过滤器链;

    清除过滤器列表等信息;

void release() {
    // 释放过滤器列表
    for (int i = 0; i < n; i++) {
        filters[i] = null;
    }

    // 清除信息
    n = 0;
    pos = 0;
    servlet = null;
    servletSupportsAsync = false;
}

上一篇:SpringBoot Shiro,解决Shiro中自定义Realm Autowired属性为空问题


下一篇:2021-11-20 shiro-core SimpleAccountRealm源码分析(三)