Spring-shiro源码陶冶-DelegatingFilterProxy和ShiroFilterFactoryBean

阅读源码有助于陶冶情操,本文旨在简单的分析shiro在Spring中的使用

介绍

Shiro是一个强大易用的Java安全框架,提供了认证、授权、加密和会话管理主要功能;另外其也提供了Web Support、缓存、Remember Me、并发等功能。
而Shiro的架构核心可看来自Java技术栈提供的图片
shiro_structure

对上述的图作简单的描述

  • Subject 用户视图,shiro对用户或者第三方服务、任何需要登录的东西统一称之为Subject对象
  • SecurityManager 安全管理器,其中内含缓存管理、会话管理,主要是对登录过来的Subject视图作一系列的安全操作(包括下述提及的Realms的关联使用)
  • Realms 数据管理器,其主要充当shiro与安全数据交互的桥梁,帮助shiro容器完成用户的校验以及认证功能。可配置多个,但必须配置至少一个。shiro也提供了默认的实现比如ladp/jdbc等方式的用户校验认证

更多的部分读者可参阅知乎Java技术栈专栏文章非常详尽的 Shiro 架构解析。本文只分析其与Spring如何搭配使用

web.xml配置Shiro环境

配置清单如下

 <!-- shiro 安全过滤器 -->
    <filter>
        <filter-name>shiroFilter</filter-name>
		<!--spring调用shiro代理-->
        <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
        <async-supported>true</async-supported>
        <init-param>
			<!--是否开启Filter的生命周期,主要涉及init和destory-->
            <param-name>targetFilterLifecycle</param-name>
            <param-value>true</param-value>
        </init-param>
    </filter>

    <filter-mapping>
        <filter-name>shiroFilter</filter-name>
        <url-pattern>/*</url-pattern>
        <dispatcher>REQUEST</dispatcher>
    </filter-mapping>

DelegatingFilterProxy#initFilterBean

入口方法,首先会执行初始化工作,shiro和spring security的代理加载实体Filter类都是通过此入口方法,代码清单如下

	@Override
	protected void initFilterBean() throws ServletException {
		synchronized (this.delegateMonitor) {
			if (this.delegate == null) {
				// If no target bean name specified, use filter name.如果没有指定则采用对应的<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);
				}
			}
		}
	}

我们主要关心DelegatingFilterProxy#initDelegate初始化委托Filter方法,代码清单如下

	protected Filter initDelegate(WebApplicationContext wac) throws ServletException {
		//根据名字获取ApplicationContext环境的bean,且必须是Filter的实现类
		Filter delegate = wac.getBean(getTargetBeanName(), Filter.class);
		//true则初始化对应的Filter类
		if (isTargetFilterLifecycle()) {
			//这里一般对应的Filter类为`org.apache.shiro.spring.web.ShiroFilterFactoryBean`
			delegate.init(getFilterConfig());
		}
		return delegate;
	}

由以上代码可以确认,application-shiro.xmlSpring配置文件必须含有与web.xmlDelegatingFilterProxy类对应的<filter-name>的bean配置

示例文件

	<!-- Shiro的Web过滤器 -->
    <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
		<!--securityManager属性必须存在,通过安全管理器调用Realm接口实现的认证/授权方法-->
        <property name="securityManager" ref="securityManager"/>
        <!--登录、主页、未通过授权页面的url-->
        <property name="loginUrl" value="/login"/>
        <property name="successUrl" value="/index.html"/>
        <property name="unauthorizedUrl" value="/403.html"/>
        <!--自定义的filter集合-->
       <property name="filters">
            <!--这里采用map集合主要是响应内部属性Map<String, Filter> filters-->
            <util:map>
                <entry key="authc" value-ref="formAuthenticationFilter"/>
                <entry key="perm" value-ref="permissionsAuthorizationFilter"/>
                <entry key="sysUser" value-ref="sysUserFilter"/>
                <entry key="user" value-ref="userFilter"/>
            </util:map>
        </property>
	<!--对应路径请求Filter链,=右边代表是filter过滤引用,且是顺序执行,url且从上置下优先匹配,一旦匹配则不往下搜寻-->
	<!--=右边表达也可为authc[role1,role2]表明访问该url必须通过认证且具有role1、role2的角色权限-->
        <property name="filterChainDefinitions">
            <value>
                /test/** = anon
                /login = captcha,authc
                /index = anon
                /403.html = anon
                /login.html = anon
                /favicon.ico = anon
                /static/** = anon
                /index.html=user,sysUser
                /welcome.html=user,sysUser
                /** = user,sysUser,perm
            </value>
        </property>
    </bean>

ShiroFilterFactoryBean#createInstance方法返回Filter实例

通过ShiroFilterFactoryBean#createInstance方法创建对应的Filter实例,我们看下创建的实例是何种人物,代码清单如下

	//创建实例,实例对象为SpringShiroFilter
	protected AbstractShiroFilter createInstance() throws Exception {

        log.debug("Creating Shiro Filter instance.");
		//<property name="securityManager">属性必须存在
        SecurityManager securityManager = getSecurityManager();
        if (securityManager == null) {
            String msg = "SecurityManager property must be set.";
            throw new BeanInitializationException(msg);
        }
		//securityManager的实现类必须是WebSecurityManager的实现类,表明Spring的shiro结合是web方面的
        if (!(securityManager instanceof WebSecurityManager)) {
            String msg = "The security manager does not implement the WebSecurityManager interface.";
            throw new BeanInitializationException(msg);
        }
		//filter过滤链管理类创建
        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);

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

其中涉及filter过滤链管理类的创建,简单分析下,代码清单如下

	protected FilterChainManager createFilterChainManager() {
		//采用默认filter过滤链管理类
        DefaultFilterChainManager manager = new DefaultFilterChainManager();
		//获取默认Filter类集合
        Map<String, Filter> defaultFilters = manager.getFilters();
        //apply global settings if necessary:
        for (Filter filter : defaultFilters.values()) {
	   //主要设置loginUrl、successUrl、unauthorizedUrl
	   //即权限Filter类设置loginUrl;认证Filter类设置successUrl、loginUrl;授权Filter类设置unauthorizedUrl,loginUrl
            applyGlobalPropertiesIfNecessary(filter);
        }

        //Apply the acquired and/or configured filters:用户自定义的Filter类,通过<property name="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();
				//同样设置url属性
                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);
            }
        }

        //build up the chains: url与对应的filter过滤链配置,解析的是<property name="filterChainDefinitions">属性
		//url可对应多个filter,且保存filter是通过ArrayList来保存的,表明过滤链则由写的先后顺序执行
        Map<String, String> chains = getFilterChainDefinitionMap();
        if (!CollectionUtils.isEmpty(chains)) {
            for (Map.Entry<String, String> entry : chains.entrySet()) {
                String url = entry.getKey();
				//chainDefinition可能为authc[role1,role2]或者authc,anno等
                String chainDefinition = entry.getValue();
				//解析以上表达式并保存至相应的自定义filter对象中
                manager.createChain(url, chainDefinition);
            }
        }

        return manager;
    }

总结

  1. Spring容器中使用Apache Shiro,需要配置

    • web.xml中配置节点,节点类为org.springframework.web.filter.DelegatingFilterProxy
    • spring配置shiro文件中需要节点,节点id名必须与web.xml定义中的节点的属性一致,且beanClass为org.apache.shiro.spring.web.ShiroFilterFactoryBean
  2. Spring配置文件中配置org.apache.shiro.spring.web.ShiroFilterFactoryBean主要设置shiro的相关filter用于拦截请求,其中的属性含义见本文的示例文件说明

下节预告

Spring-shiro源码陶冶-DefaultFilter

posted @ 2017-04-28 19:45  南柯问天  阅读(2902)  评论(0编辑  收藏  举报