shiro学习笔记:整合spring之拦截器链执行流程

一、环境准备

搭建好spring + shiro整合环境(本文环境Spring 4.3.10.RELEASE + Shiro 1.4.0)后,编写登录页面如下:
<html>
<head>
<title>登录页</title>
</head>
<body>
<div style="color:red;">${shiroLoginFailure}</div>
	<form action="" method="post">
		用户名:<input type="text" name="username"><br /> 
		密码:<input type="password" name="password"><br /> 
		<input type="submit" value="登录">
	</form>
</body>
</html>
shiro拦截器部分配置:
    <!-- 基于Form表单的身份验证过滤器 -->
    <bean id="formAuthenticationFilter" class="org.apache.shiro.web.filter.authc.FormAuthenticationFilter">
        <property name="usernameParam" value="username"/>
        <property name="passwordParam" value="password"/>
        <property name="loginUrl" value="/login.jsp"/>
    </bean>
    
    <!-- Shiro的Web过滤器:此处使用ShiroFilterFactoryBean来创建ShiroFilter过滤器 -->
    <!-- Bean id必须和web.xml文件中配置的DelegatingFilterProxy的filter-name一致 -->
    <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
        <property name="securityManager" ref="securityManager"/>
        <property name="loginUrl" value="/login.jsp"/>
        <property name="unauthorizedUrl" value="/unauthorized.jsp"/>
        <property name="filters">
            <util:map>
                <entry key="authc" value-ref="formAuthenticationFilter"/>
            </util:map>
        </property>
        <property name="filterChainDefinitions">
            <value>
                /index.jsp = anon
                /unauthorized.jsp = anon
                /error.jsp = anon
                /login.jsp = authc
                /logout = logout
                /** = user
            </value>
        </property>
    </bean>
shiro拦截器树状结构如下图:

其中几个主要的拦截:OnceperRequestFilter、AbstractShiroFilter、AdviceFilter、PathMatchingFilter、AccessControlFilter、AuthenticationFilter、AuthorizationFilter等构成shiro的拦截器基本架构。

(-- 关于各shiro各个拦截器的简单介绍可参考博文:Apache Shiro学习笔记(六)Shiro Filter介绍

二、初始化过程

启动项目时spring首先会通过doGetObjectFromFactoryBean()方法来初始化Shiro的拦截器入口工厂类,即org.apache.shiro.spring.web.ShiroFilterFactoryBean:
/**
 * Obtain an object to expose from the given FactoryBean.
 * @param factory the FactoryBean instance
 * @param beanName the name of the bean
 * @return the object obtained from the FactoryBean
 * @throws BeanCreationException if FactoryBean object creation failed
 * @see org.springframework.beans.factory.FactoryBean#getObject()
 */
private Object doGetObjectFromFactoryBean(final FactoryBean<?> factory, 
	final String beanName) throws BeanCreationException {
	Object object;
	try {
		if (System.getSecurityManager() != null) {
			AccessControlContext acc = getAccessControlContext();
			try {
				object = AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() {
					@Override
					public Object run() throws Exception {
						return factory.getObject();
					}
				}, acc);
			} catch (PrivilegedActionException pae) {
				throw pae.getException();
			}
		} else {
			// 调用工厂方法生成实例
			object = factory.getObject();
		}
	} catch (FactoryBeanNotInitializedException ex) {
		throw new BeanCurrentlyInCreationException(beanName, ex.toString());
	} catch (Throwable ex) {
		throw new BeanCreationException(beanName, "FactoryBean threw exception on object creation", ex);
	}
	// Do not accept a null value for a FactoryBean that's not fully
	// initialized yet: Many FactoryBeans just return null then.
	if (object == null && isSingletonCurrentlyInCreation(beanName)) {
		throw new BeanCurrentlyInCreationException(beanName, "FactoryBean which is currently in creation returned null from getObject");
	}
	return object;
}
ShiroFilterFactoryBean提供的获取实例的工厂方法:
/**
 * Lazily creates and returns a {@link AbstractShiroFilter} concrete instance via the
 * {@link #createInstance} method.
 *
 * @return the application's Shiro Filter instance used to filter incoming web requests.
 * @throws Exception if there is a problem creating the {@code Filter} instance.
 */
public Object getObject() throws Exception {
    if (instance == null) {
        instance = createInstance();
    }
    return instance;
}
最终会通过调用ShiroFilterFactoryBean的createInstance()方法初始化shiro拦截器入口类:
protected AbstractShiroFilter createInstance() throws Exception {

    log.debug("Creating Shiro Filter instance.");
    // 获取的是配置的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);
    }
    // 创建负责维护URL模式与拦截器链关系的DefaultFilterChainManager实例
    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:
    // 创建shiro提供的唯一FilterChainResolver实现,用于解析访问路径所对应的拦截器链
    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:
    // 最终返回的是SpringShiroFilter实例(ShiroFilterFactoryBean的内部类)
    return new SpringShiroFilter((WebSecurityManager) securityManager, chainResolver);
}
接下来看看shiro在createFilterChainManager()方法如何创建FilterChainManager实例:
protected FilterChainManager createFilterChainManager() {
    // 创建的是shiro默认的DefaultFilterChainManager实例(构造方法内初始化默认拦截器)
    DefaultFilterChainManager manager = new DefaultFilterChainManager();
    // 获取shiro默认的拦截器Map<拦截路径,拦截器>映射集合
    Map<String, Filter> defaultFilters = manager.getFilters();
    // Apply global settings if necessary:(为默认拦截器设置通用属性例如loginUrl,unauthroizedUrl等)
    for (Filter filter : defaultFilters.values()) {
        applyGlobalPropertiesIfNecessary(filter);
    }
    // Apply the acquired and/or configured filters:获取配置文件中filters属性配置的拦截器Map集合
    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:
            // 将自定义拦截器加入FilterChainManager中的拦截器Map<拦截器名,拦截器实例>
            manager.addFilter(name, filter, false);
        }
    }
    // build up the chains:获取自定义拦截器链Map交由Manager管理(对应的配置文件属性是filterChainDefinitions)
    Map<String, String> chains = getFilterChainDefinitionMap();
    if (!CollectionUtils.isEmpty(chains)) {
        for (Map.Entry<String, String> entry : chains.entrySet()) {
            String url = entry.getKey();// 配置的URL
            String chainDefinition = entry.getValue();// 该URL对应的拦截器实例
            // 解析filterChainDefinitions配置:每一个配置的URL对应一个shiro代理拦截器链
            // 并将解析的每一个拦截器链交由DefaultFilterChainManager的Map<URL路径,拦截器链>管理
            manager.createChain(url, chainDefinition);
        }
    }
    return manager;
}
shiro默认拦截器链管理器DefaultFilterChainManager的初始化构造方法:
// DefaultFilterChainManager在初始化时添加默认拦截器
public DefaultFilterChainManager() {
    // 此处初始化拦截器Map<拦截器名称,拦截器实例>:这里保存所有的默认以及自定义的拦截器
    this.filters = new LinkedHashMap<String, Filter>();
    // 此处初始化拦截器链Map<拦截器链名称(拦截路径),拦截器集合>:这里保存所有自定义的拦截器链所对应的拦截器集合
    this.filterChains = new LinkedHashMap<String, NamedFilterList>();
    addDefaultFilters(false);
}

// DefaultFilter是一个枚举类,定义了shiro的所有默认拦截器
protected void addDefaultFilters(boolean init) {
    for (DefaultFilter defaultFilter : DefaultFilter.values()) {
    	// 将所有默认拦截器加入filters集合中
        addFilter(defaultFilter.name(), defaultFilter.newInstance(), init, false);
    }
}
启动时解析拦截器链定义的set方法:
/**
 * A convenience method that sets the {@link #setFilterChainDefinitionMap(java.util.Map) filterChainDefinitionMap}
 * property by accepting a {@link java.util.Properties Properties}-compatible string (multi-line key/value pairs).
 * Each key/value pair must conform to the format defined by the
 * {@link FilterChainManager#createChain(String,String)} JavaDoc - each property key is an ant URL
 * path expression and the value is the comma-delimited chain definition.
 *
 * @param definitions a {@link java.util.Properties Properties}-compatible string (multi-line key/value pairs)
 *                    where each key/value pair represents a single urlPathExpression-commaDelimitedChainDefinition.
 */
// ShiroFilterFactoryBean的setFilterChainDefinitions方法:解析配置的"filterChainDefinitions"属性
public void setFilterChainDefinitions(String definitions) {
    Ini ini = new Ini();
    ini.load(definitions);
    //did they explicitly state a 'urls' section?  Not necessary, but just in case:
    Ini.Section section = ini.getSection(IniFilterChainResolverFactory.URLS);
    if (CollectionUtils.isEmpty(section)) {
        //no urls section.  Since this _is_ a urls chain definition property, just assume the
        //default section contains only the definitions:
        section = ini.getSection(Ini.DEFAULT_SECTION_NAME);
    }
    // 最后解析为Map<拦截路径,拦截器链定义>并调用set方法初始化
    setFilterChainDefinitionMap(section);
}

/**
 * Sets the chainName-to-chainDefinition map of chain definitions to use for creating filter chains intercepted
 * by the Shiro Filter.  Each map entry should conform to the format defined by the
 * {@link FilterChainManager#createChain(String, String)} JavaDoc, where the map key is the chain name (e.g. URL
 * path expression) and the map value is the comma-delimited string chain definition.
 *
 * @param filterChainDefinitionMap the chainName-to-chainDefinition map of chain definitions to use for creating
 *                                 filter chains intercepted by the Shiro Filter.
 */
// ShiroFilterFactoryBean的set方法:初始化拦截器定义Map
public void setFilterChainDefinitionMap(Map<String, String> filterChainDefinitionMap) {
    this.filterChainDefinitionMap = filterChainDefinitionMap;
}
Shiro声明的默认拦截器枚举类:
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 final Class<? extends Filter> filterClass;

    private DefaultFilter(Class<? extends Filter> filterClass) {
        this.filterClass = filterClass;
    }

    public Filter newInstance() {
        return (Filter) ClassUtils.newInstance(this.filterClass);
    }

    public Class<? extends Filter> getFilterClass() {
        return this.filterClass;
    }

    public static Map<String, Filter> createInstanceMap(FilterConfig config) {
        Map<String, Filter> filters = new LinkedHashMap<String, Filter>(values().length);
        for (DefaultFilter defaultFilter : values()) {
            Filter filter = defaultFilter.newInstance();
            if (config != null) {
                try {
                    filter.init(config);
                } catch (ServletException e) {
                    String msg = "Unable to correctly 
  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值