shiro源码分析之shiroFilter初始化过程

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的过滤器走完后,会继续走其它的拦截器。
个人见解,不喜勿喷,若有错误,欢迎指正。

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值