shiro源码分析之shiroFilter初始化过程
1、首先看使用shiro(1.3.0)框架要使用的配置。
- 配置web.xml
<!-- shiro 安全过滤器滤器 start-->
<!-- Make sure any request you want accessible to Shiro is filtered. /* catches all -->
<!-- requests. Usually this filter mapping is defined first (before all others) to -->
<!-- ensure that Shiro works in subsequent filters in the filter chain: -->
<filter-mapping>
<filter-name>shiroFilter</filter-name>
<url-pattern>*</url-pattern>
</filter-mapping>
<!-- The filter-name matches name of a 'shiroFilter' bean inside applicationContext.xml -->
<filter>
<filter-name>shiroFilter</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
<!-- <async-supported>true</async-supported> -->
<init-param>
<param-name>targetFilterLifecycle</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<!-- shiro 安全过滤器滤器 end-->
- 配置SpinngMVC.xml(本部分可以以后再配置)
<!-- 开启Shiro的注解(如@RequiresRoles,@RequiresPermissions) start,需借助SpringAOP扫描使用Shiro注解的类,并在必要时进行安全逻辑验证 -->
<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator" depends-on="lifecycleBeanPostProcessor">
<property name="proxyTargetClass" value="true" />
</bean>
<!-- 开启Shiro的注解end -->
- 配置Spring.xml
<!--
Shiro主过滤器本身功能十分强大,其强大之处就在于它支持任何基于URL路径表达式的、自定义的过滤器的执行
Web应用中,Shiro可控制的Web请求必须经过Shiro主过滤器的拦截,Shiro对基于Spring的Web应用提供了完美的支持
-->
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<!-- Shiro的核心安全接口,这个属性是必须的 -->
<property name="securityManager" ref="securityManager"/>
<!-- 要求登录时的链接(登录页面地址),非必须的属性,默认会自动寻找Web工程根目录下的"/login.jsp"页面 -->
<property name="loginUrl" value="/login.html"/>
<!-- 登录成功后要跳转的连接(本例中此属性用不到,因为登录成功后的处理逻辑在LoginController里硬编码) -->
<!-- <property name="successUrl" value="/" ></property> -->
<!-- 用户访问未对其授权的资源时,所显示的连接 -->
<property name="unauthorizedUrl" value="/"></property>
<property name="filterChainDefinitions">
<value>
/login.html = anon
</value>
</property>
<!-- 自定义拦截器 -->
<!-- <property name="filters">
<util:map>
<entry key="authc" value-ref="formAuthenticationFilter"/>
</util:map>
</property> -->
</bean>
<!-- Shiro生命周期处理器-->
<bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/>
<!-- 相当于调用SecurityUtils.setSecurityManager(securityManager) -->
<bean class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
<property name="staticMethod" value="org.apache.shiro.SecurityUtils.setSecurityManager"/>
<property name="arguments" ref="securityManager"/>
</bean>
<bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
<property name="securityManager" ref="securityManager" />
</bean>
到此,简单的配置到shiroFilter结束,其他的都是在此基础之上配置,在这里先不做分析。那么开始从web.xml开始分析。
2、先看web.xml中shiro配置的官方注释:Make sure any request you want accessible to Shiro is filtered. /* catches all requests. Usually this filter mapping is defined first (before all others) to ensure that Shiro works in subsequent filters in the filter chain。大意就是配置映射过滤路径。
然后看org.springframework.web.filter.DelegatingFilterProxy这个Filter代理做了那些事情。在这个类中有一个initFilterBean方法。见名思意就是初始化Filter。
@Override 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); } } } }
注:targetBeanName就是目标bean的名字,通过getFilterName()获取web.xml的配置的FilterName即shiroFilter。而调用initFilterBean()是在该类初始化的init()方法中.init在GenericFilterBean中。
/** * Standard way of initializing this filter. * Map config parameters onto bean properties of this filter, and * invoke subclass initialization. * @param filterConfig the configuration for this filter * @throws ServletException if bean properties are invalid (or required * properties are missing), or if subclass initialization fails. * @see #initFilterBean */ @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. try { PropertyValues pvs = new FilterConfigPropertyValues(filterConfig, this.requiredProperties); BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this); ResourceLoader resourceLoader = new ServletContextResourceLoader(filterConfig.getServletContext()); bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, this.environment)); 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"); } }
然后是这个initDelegate()这个方法的代码:
protected Filter initDelegate(WebApplicationContext wac) throws ServletException { Filter delegate = wac.getBean(getTargetBeanName(), Filter.class); if (isTargetFilterLifecycle()) { delegate.init(getFilterConfig()); } return delegate; }
注:通过getBean方法实例化一个Filter,然后判断isTargetFilterLifecycle值(是否初始化使用,这个值在web.xml中配置为true)。
再看看delegate.init(getFilterConfig())做了什么,delegate此时是Filter,org.apache.shiro.web.servlet.AbstractFilter这个类中init()方法。
public final void init(FilterConfig filterConfig) throws ServletException { this.setFilterConfig(filterConfig); try { this.onFilterConfigSet(); } catch (Exception arg2) { if (arg2 instanceof ServletException) { throw (ServletException) arg2; } else { if (log.isErrorEnabled()) { log.error("Unable to start Filter: [" + arg2.getMessage() + "].", arg2); } throw new ServletException(arg2); } } }
在onFilterConfigSet()中:
protected final void onFilterConfigSet() throws Exception { this.applyStaticSecurityManagerEnabledConfig(); this.init(); this.ensureSecurityManager(); if (this.isStaticSecurityManagerEnabled()) { SecurityUtils.setSecurityManager(this.getSecurityManager()); } }
此时,我们已经看到SecurityUtils的影子,也知道在web项目中可以直接通过SecurityUtils调用的Subject在什么时候初始化,但是还没看到Filter的影子,好继续往下面看,点进init()中终于我们来到org.apache.shiro.web.servlet.ShiroFilter类中的init方法:
public void init() throws Exception { WebEnvironment env = WebUtils.getRequiredWebEnvironment(this.getServletContext()); this.setSecurityManager(env.getWebSecurityManager()); FilterChainResolver resolver = env.getFilterChainResolver(); if (resolver != null) { this.setFilterChainResolver(resolver); } }
在这里,获取了环境的FilterChainResolver。然后回到DelegatingFilterProxy类中,此时没次请求都经过doFilter方法:
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
// Lazily initialize the delegate if necessary.
Filter delegateToUse = this.delegate;
if (delegateToUse == null) {
synchronized (this.delegateMonitor) {
if (this.delegate == null) {
WebApplicationContext wac = findWebApplicationContext();
if (wac == null) {
throw new IllegalStateException("No WebApplicationContext found: " +
"no ContextLoaderListener or DispatcherServlet registered?");
}
this.delegate = initDelegate(wac);
}
delegateToUse = this.delegate;
}
}
// Let the delegate perform the actual doFilter operation.
invokeDelegate(delegateToUse, request, response, filterChain);
}
注:可以看到此时,直接调用DelegatingFilterProxy的delegateToUse获取Filter,然后进入invokeDelegate方法。
在invokeDelegate方法中,我们看到:
protected void invokeDelegate( Filter delegate, ServletRequest request, ServletResponse response, FilterChain filterChain) throws ServletException, IOException { delegate.doFilter(request, response, filterChain); }
继续进入方法看:
org.apache.shiro.web.servlet.OncePerRequestFilter中的doFilter方法:
public final void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws ServletException, IOException { String alreadyFilteredAttributeName = this.getAlreadyFilteredAttributeName(); if (request.getAttribute(alreadyFilteredAttributeName) != null) { log.trace("Filter \'{}\' already executed. Proceeding without invoking this filter.", this.getName()); filterChain.doFilter(request, response); } else if (this.isEnabled(request, response) && !this.shouldNotFilter(request)) { log.trace("Filter \'{}\' not yet executed. Executing now.", this.getName()); request.setAttribute(alreadyFilteredAttributeName, Boolean.TRUE); try { this.doFilterInternal(request, response, filterChain); } finally { request.removeAttribute(alreadyFilteredAttributeName); } } else { log.debug("Filter \'{}\' is not enabled for the current request. Proceeding without invoking this filter.", this.getName()); filterChain.doFilter(request, response); } }
filterChain在项目中有四个实现:MockFilterChain、PassThroughFilterChain、ProxiedFilterChain、VirtualFilterChain,继续进入filterChain.doFilter(request, response),会来到shiro的拦截器链org.apache.shiro.web.servlet.ProxiedFilterChain,终于找到了shiro怎样处理自己配置的Filter了:
>
public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException { if (this.filters != null && this.filters.size() != this.index) { if (log.isTraceEnabled()) { log.trace("Invoking wrapped filter at index [" + this.index + "]"); } ((Filter) this.filters.get(this.index++)).doFilter(request, response, this); } else { if (log.isTraceEnabled()) { log.trace("Invoking original filter chain."); } this.orig.doFilter(request, response); } }
当shiro的过滤器走完后,会继续走其它的拦截器。
个人见解,不喜勿喷,若有错误,欢迎指正。