这次的入口是DelegatingFilterProxy类,其在web.xml中以filter的配置存在,先看一下配置:
<filter>
<filter-name>shiroFilter</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
<!-- 设置true由servlet容器控制filter的生命周期 -->
<init-param>
<param-name>targetFilterLifecycle</param-name>
<param-value>true</param-value>
</init-param>
<!-- 设置spring容器filter的bean id,如果不设置则找与filter-name一致的bean-->
<init-param>
<param-name>targetBeanName</param-name>
<param-value>shiroFilter</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>shiroFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
其主要就有两个配置targetFilterLifecycle(目标过滤器生命周期)和targetBeanName(目标bean的名称)
既然作为filter那么必然有一系列的方法,比如init,doFilter,destory,让我们跟踪进DelegatingFilterProxy类,
public class DelegatingFilterProxy extends GenericFilterBean
其继承了GenericFilterBean,并且发现DelegatingFilterProxy没有init方法,再跟踪进GenericFilterBean方法,继承关系如下
public abstract class GenericFilterBean implements Filter, BeanNameAware, EnvironmentAware,
EnvironmentCapable, ServletContextAware, InitializingBean, DisposableBean
其只是实现了一些接口,所以GenericFilterBean为基类,因为继承关系,在DelegatingFilterProxy初始化时调用的是GenericFilterBean的init方法,init代码如下:
@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;
// 加载web.xml中的init参数,组装为PropertyValue对象,PropertyValues是对PropertyValue的封装
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);
}
}
// 子类DelegatingFilterProxy初始化
initFilterBean();
if (logger.isDebugEnabled()) {
logger.debug("Filter '" + filterConfig.getFilterName() + "' configured successfully");
}
}
追踪到DelegatingFilterProxy的initFilterBean方法中,代码如下
protected void initFilterBean() throws ServletException {
synchronized (this.delegateMonitor) {
if (this.delegate == null) {
// 这里判断targetBeanName是否配置,如果没有则用filter-name配置的值去spring容器中去查找具体的bean(这个值是在加载init的参数时通过反射设置的)
if (this.targetBeanName == null) {
this.targetBeanName = getFilterName();
}
// 这里是获取spring的IOC容器,为后续获取bean做准备
WebApplicationContext wac = findWebApplicationContext();
if (wac != null) {
this.delegate = initDelegate(wac);//初始化filter
}
}
}
}
在看initDelegate之前先看下,spring中shiro的配置bean:
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
initDelegate方法如下:
protected Filter initDelegate(WebApplicationContext wac) throws ServletException {
//这里根据配置的targetBeanName去spring容器中获取bean,这里是通过shiroFilter去搜索ShiroFilterFactoryBean,这里检索到的是factoryBean,并不是普通的bean,这里返回的是由ShiroFilterFactoryBean的getObject方法得到的对象
Filter delegate = wac.getBean(getTargetBeanName(), Filter.class);
if (isTargetFilterLifecycle()) {
//初始化Filter,AbstractFilter与AbstractShiroFilter
delegate.init(getFilterConfig());
}
return delegate;
}
初始化完成,但是我在上面的init方法中并没有找到一些更有用的东西,这里delegate是SpringShiroFilter类,init调用的是父类
AbstractFilter的init方法,init如下:
public final void init(FilterConfig filterConfig) throws ServletException {
setFilterConfig(filterConfig);
try {
//子类实现,可以进行初始化
onFilterConfigSet();
} catch (Exception e) {
if (e instanceof ServletException) {
throw (ServletException) e;
} else {
if (log.isErrorEnabled()) {
log.error("Unable to start Filter: [" + e.getMessage() + "].", e);
}
throw new ServletException(e);
}
}
}
这里并没有太多事,把filterConfig设置到本类中,并且让子类初始化,跟进onFilterConfigSet,在AbstractFilter的子类AbstractShiroFilter中:
protected final void onFilterConfigSet() throws Exception {
//added in 1.2 for SHIRO-287:
applyStaticSecurityManagerEnabledConfig();
init();
ensureSecurityManager();
//added in 1.2 for SHIRO-287:
if (isStaticSecurityManagerEnabled()) {
SecurityUtils.setSecurityManager(getSecurityManager());
}
}
发现这里有init方法,但是不好意思这里是空的,init是没用的。可以看到这里只初始化了AbstractFilter与AbstractShiroFilter,那shiro的自己的拦截器是哪里加载的,拦截器链式怎么组装的呢?
真是一脸懵逼,开始我以为是在初次访问servlet时初始化,但是明显不对,因为调用时已经开始用拦截器链了。那是不是还有些地方初始化被丢下了?后来又跟踪了一遍,发现了确实有。
上面我们提到,我们在spring容器中获取factoryBean时,并不是获取这个Bean而是通过factoryBean的getObject方法返回的对象,就是在调用getObject方法构造对象时进行了shiro的拦截器加载,拦截器链组装。
这里就不带大家看怎么由getBean来调用getObject,无非就是取得Bean之后,判断该Bean是否为factoryBean,如果是通过反射调用该bean的getObject方法,然后返回对象。
下面重点看ShiroFilterFactoryBean这个类:
getObject方法:
public Object getObject() throws Exception {
if (instance == null) {
instance = createInstance();
}
return instance;
}
createInstance方法,所有的工作都在这个方法进行,返回的是SpringShiroFilter类
protected AbstractShiroFilter createInstance() throws Exception {
//获取安全管理器,在配置文件中配置的DefaultWebSecurityManager
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();
PathMatchingFilterChainResolver chainResolver = new PathMatchingFilterChainResolver();
chainResolver.setFilterChainManager(manager);
return new SpringShiroFilter((WebSecurityManager) securityManager, chainResolver);
}
下面重点看createFilterChainManager方法:
protected FilterChainManager createFilterChainManager() {
//这里用的是默认的,在创建的时候,构造方法内执行了addDefaultFilters方法,目的是从DefaultFilter这个枚举类
//中,加载设置好的默认拦截器,put到自己的filters中
DefaultFilterChainManager manager = new DefaultFilterChainManager();
//获取DefaultFilterChainManager的默认拦截器
Map<String, Filter> defaultFilters = manager.getFilters();
//设置全局参数
for (Filter filter : defaultFilters.values()) {
applyGlobalPropertiesIfNecessary(filter);
}
//这里是设置自己的拦截器,并添加到拦截器链管理器
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);
}
manager.addFilter(name, filter, false);
}
}
//build up the chains:
//这里获取配置文件的filterChainDefinitions,以map形式存在,比如配置了/images/**=anon,则key为/images/**,value为anon
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);
}
}
return manager;
}
public DefaultFilterChainManager() {
this.filters = new LinkedHashMap<String, Filter>();
this.filterChains = new LinkedHashMap<String, NamedFilterList>();
addDefaultFilters(false);
}
protected void addDefaultFilters(boolean init) {
for (DefaultFilter defaultFilter : DefaultFilter.values()) {
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);
private void applyGlobalPropertiesIfNecessary(Filter filter) {
applyLoginUrlIfNecessary(filter);
applySuccessUrlIfNecessary(filter);
applyUnauthorizedUrlIfNecessary(filter);
}
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);
}
}
<!-- 过虑器链定义,从上向下顺序执行,一般将/**放在最下边 -->
<property name="filterChainDefinitions">
<value>
/images/**=anon
/js/**=anon
/styles/**=anon
<!-- 退出拦截,请求logout.action执行退出操作 -->
/logout.action = logout
<!-- user表示身份认证通过或通过记住我认证通过的可以访问 -->
/** = authc
</value>
</property>
public void createChain(String chainName, String chainDefinition) {
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]" }
//
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) {
String[] nameConfigPair = toNameConfigPair(token);
//now we have the filter name, path and (possibly null) path-specific config. Let's apply them:
addToChain(chainName, nameConfigPair[0], nameConfigPair[1]);
}
}
public void addToChain(String chainName, String filterName, String chainSpecificFilterConfig) {
if (!StringUtils.hasText(chainName)) {
throw new IllegalArgumentException("chainName cannot be null or empty.");
}
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).");
}
applyChainConfig(chainName, filter, chainSpecificFilterConfig);
NamedFilterList chain = ensureChain(chainName);
chain.add(filter);
}
protected void applyChainConfig(String chainName,Filter filter,String chainSpecificFilterConfig) {
if (log.isDebugEnabled()) {
log.debug("Attempting to apply path [" + chainName + "] to filter [" + filter + "] " +
"with config [" + chainSpecificFilterConfig + "]");
}
if (filter instanceof PathConfigProcessor) {
((PathConfigProcessor) filter).processPathConfig(chainName, chainSpecificFilterConfig);
} else {
if (StringUtils.hasText(chainSpecificFilterConfig)) {
//they specified a filter configuration, but the Filter doesn't implement PathConfigProcessor
//this is an erroneous config:
String msg = "chainSpecificFilterConfig was specified, but the underlying " +
"Filter instance is not an 'instanceof' " +
PathConfigProcessor.class.getName() + ". This is required if the filter is to accept " +
"chain-specific configuration.";
throw new ConfigurationException(msg);
}
}
}
public Filter processPathConfig(String path, String config) {
String[] values = null;
if (config != null) {
values = split(config);
}
this.appliedPaths.put(path, values);
return this;
}
protected NamedFilterList ensureChain(String chainName) {
NamedFilterList chain = getChain(chainName);
if (chain == null) {
chain = new SimpleNamedFilterList(chainName);
this.filterChains.put(chainName, chain);
}
return chain;
}
到此差不多就结束了,剩下最后一点,回到createInstance方法中只剩下下面代码:
PathMatchingFilterChainResolver chainResolver = new PathMatchingFilterChainResolver();
chainResolver.setFilterChainManager(manager);
return new SpringShiroFilter((WebSecurityManager) securityManager, chainResolver);
protected SpringShiroFilter(WebSecurityManager webSecurityManager, FilterChainResolver resolver) {
super();
if (webSecurityManager == null) {
throw new IllegalArgumentException("WebSecurityManager property cannot be null.");
}
setSecurityManager(webSecurityManager);
if (resolver != null) {
setFilterChainResolver(resolver);
}
}