Shiro使用和源码分析---1

来源:互联网 发布:武汉java培训班 编辑:程序博客网 时间:2024/05/22 06:45

Shiro使用和原理分析—1

网上有很多介绍shiro框架的文章,但是没有讲解shiro如何和web spring框架相结合的文章。由于实际项目的需要,这里首先顺带分析一下shiro中FormAuthenticationFilter的源码。

先看一段Spring中applicationContext.xml的配置。

    <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">        <property name="securityManager" ref="securityManager"/>        <property name="filters">            <util:map>                <entry key="authc" value-ref="customAuthenticationFilter"/>            </util:map>        </property>        <property name="filterChainDefinitions">            <value>                  /test = anon                /** = authc            </value>        </property>    </bean>

下面就看一下ShiroFilterFactoryBean。

ShiroFilterFactoryBean

public class ShiroFilterFactoryBean implements FactoryBean, BeanPostProcessor

ShiroFilterFactoryBean的构造函数

    public ShiroFilterFactoryBean() {        this.filters = new LinkedHashMap<String, Filter>();        this.filterChainDefinitionMap = new LinkedHashMap<String, String>(); //order matters!    }

很简单,再来看几个set函数,Spring框架会根据这几个set函数注入相应的bean。

    public void setSecurityManager(SecurityManager securityManager) {        this.securityManager = securityManager;    }    public void setFilters(Map<String, Filter> filters) {        this.filters = filters;    public void setFilterChainDefinitions(String definitions) {        Ini ini = new Ini();        ini.load(definitions);        Ini.Section section = ini.getSection(IniFilterChainResolverFactory.URLS);        if (CollectionUtils.isEmpty(section)) {            section = ini.getSection(Ini.DEFAULT_SECTION_NAME);        }        setFilterChainDefinitionMap(section);    }

这里根据applicationContext.xml设置了securityManager,filters(customAuthenticationFilter)。还有setFilterChainDefinitions函数,传入它的String参数definitions便是“/test = anon,/** = authc”,该函数读取这些配置,构造相应的section,并放入filterChainDefinitionMap中,这里就不详细分析里面的函数了。

注意注意,由于ShiroFilterFactoryBean实现了FactoryBean接口,上层通过getBean方法返回的不是ShiroFilterFactoryBean本身,而是 ShiroFilterFactoryBean的getObject()方法所返回的对象,相当于ShiroFilterFactoryBean的getObject()方法代理了getBean()方法。返回的对象类型由getObjectType()方法指定,是否为单例由方法isSingleton()指定。下面一口气看ShiroFilterFactoryBean中的这三个函数,

    private AbstractShiroFilter instance;    public Object getObject() throws Exception {        if (instance == null) {            instance = createInstance();        }        return instance;    }    public Class getObjectType() {        return SpringShiroFilter.class;    }    public boolean isSingleton() {        return true;    }

因此通过Spring配置文件构造的其实是SpringShiroFilter这个过滤器,构造它的函数在createInstance中。

createInstance

    protected AbstractShiroFilter createInstance() throws Exception {        log.debug("Creating Shiro Filter instance.");        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);    }

createInstance里面就是构造了一个FilterChainManager和PathMatchingFilterChainResolver,然后将FilterChainManager设置到PathMatchingFilterChainResolver中。下面一一来看。PathMatchingFilterChainResolver就是一个简单的构造函数,如下所示

    public PathMatchingFilterChainResolver() {        this.pathMatcher = new AntPathMatcher();        this.filterChainManager = new DefaultFilterChainManager();    }

下面重点分析createFilterChainManager这个函数。

createFilterChainManager

    protected FilterChainManager createFilterChainManager() {        DefaultFilterChainManager manager = new DefaultFilterChainManager();        Map<String, Filter> defaultFilters = manager.getFilters();        //apply global settings if necessary:        for (Filter filter : defaultFilters.values()) {            applyGlobalPropertiesIfNecessary(filter);        }        //Apply the acquired and/or configured 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();                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:        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;    }

首先构造了一个DefaultFilterChainManager,如下所示

    public DefaultFilterChainManager() {        this.filters = new LinkedHashMap<String, Filter>();        this.filterChains = new LinkedHashMap<String, NamedFilterList>();        addDefaultFilters(false);    }

addDefaultFilters用于添加默认的过滤器,参数false表示不对这些添加的过滤器进行初始化。

    protected void addDefaultFilters(boolean init) {        for (DefaultFilter defaultFilter : DefaultFilter.values()) {            addFilter(defaultFilter.name(), defaultFilter.newInstance(), init, false);        }    }

那么默认的过滤器都有那些呢?这里简单看一下。

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

回到createFilterChainManager函数,接下来做了三件事情,第一是对每一个默认的过滤器调用applyGlobalPropertiesIfNecessary进行设置,第二是对每一个自定义的过滤器进行设置并添加到过滤器管理器DefaultFilterChainManager中,第三是调用createChain构造chain(这里叫它过滤器链吧)。过滤器链是什么,就是记录一个url和过滤器之间一对多的关系。例如前面在applicationContext.xml中设置的“/test = anon,/** = authc”,这里的url指的就是“/test”和“/**”,过滤器则由anon和authc来指定。

    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 + "]");        }        String[] filterTokens = splitChainDefinition(chainDefinition);        for (String token : filterTokens) {            String[] nameConfigPair = toNameConfigPair(token);            addToChain(chainName, nameConfigPair[0], nameConfigPair[1]);        }    }

splitChainDefinition用于将url和过滤器名字分开,例如/test = anon就变为/test和anon。toNameConfigPair则是将anon这个字段进一步分开,因为这里只有anon,调用过后nameConfigPair[0]=anon,nameConfigPair[1]=null。然后调用addToChain添加到过滤器链中。

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

getFilter根据过滤器名称获取过滤器,例如这里为anon,则取出AnonymousFilter这个过滤器。applyChainConfig将chainName和chainSpecificFilterConfig设置到filter的成员变量appliedPaths里,第二章会用到这个appliedPaths。
NamedFilterList就代表了一个过滤器链了,ensureChain的代码如下

    protected NamedFilterList ensureChain(String chainName) {        NamedFilterList chain = getChain(chainName);        if (chain == null) {            chain = new SimpleNamedFilterList(chainName);            this.filterChains.put(chainName, chain);        }        return chain;    }

ensureChain其实就是构造了一个SimpleNamedFilterList,然后设置到filterChains中。
回到addToChain中,最后就是将过滤器添加到ensureChain中构造的SimpleNamedFilterList中。

PathMatchingFilterChainResolver

最后构造一个SpringShiroFilter,看一下SpringShiroFilter的整个类。

    private static final class SpringShiroFilter extends AbstractShiroFilter {        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);            }        }    }

这个构造函数就是简单的赋值。AbstractShiroFilter里面有个重要的doFilterInternal函数,下一章再来分析这个函数。

这里重点再看一下ShiroFilterFactoryBean实现的另一个接口BeanPostProcessor,网上有很多介绍这个接口的文章。如http://uule.iteye.com/blog/2094549所述:

BeanPostProcessor接口作用是:如果我们需要在Spring容器完成Bean的实例化、配置和其他的初始化前后添加一些自己的逻辑处理,我们就可以定义一个或者多个BeanPostProcessor接口的实现,然后注册到容器中。
这里写图片描述
因此Spring中的BeanPostProcessor在实例化过程处于的位置,BeanPostProcessor接口有两个方法需要实现:postProcessBeforeInitialization和postProcessAfterInitialization。postProcessAfterInitialization为空函数,因此回过头来看一下ShiroFilterFactoryBean中的这个函数。

postProcessBeforeInitialization

    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {        if (bean instanceof Filter) {            log.debug("Found filter chain candidate filter '{}'", beanName);            Filter filter = (Filter) bean;            applyGlobalPropertiesIfNecessary(filter);            getFilters().put(beanName, filter);        } else {            log.trace("Ignoring non-Filter bean '{}'", beanName);        }        return bean;    }

这里传入的filter便是前面在applicationContext.xml中定义的customAuthenticationFilter。applyGlobalPropertiesIfNecessary主要和设置url有关,然后将该filter放入ShiroFilterFactoryBean管理的map中。

applyGlobalPropertiesIfNecessary

    private void applyGlobalPropertiesIfNecessary(Filter filter) {        applyLoginUrlIfNecessary(filter);        applySuccessUrlIfNecessary(filter);        applyUnauthorizedUrlIfNecessary(filter);    }

applyGlobalPropertiesIfNecessary主要就做一件事情,就是设置customAuthenticationFilter中的loginUrl,SuccessUrl和unauthorizedUrl。(由于CustomAuthenticationFilter主要和验证模块相关,并没有继承自AuthorizationFilter,因此关于授权的url就为空)

applyLoginUrlIfNecessary

    private void applyLoginUrlIfNecessary(Filter filter) {        String loginUrl = getLoginUrl();        if (StringUtils.hasText(loginUrl) && (filter instanceof AccessControlFilter)) {            AccessControlFilter acFilter = (AccessControlFilter) filter;            String existingLoginUrl = acFilter.getLoginUrl();            if (AccessControlFilter.DEFAULT_LOGIN_URL.equals(existingLoginUrl)) {                acFilter.setLoginUrl(loginUrl);            }        }    }

这里简单来说就是做一件事情,如果customAuthenticationFilter中调用的getLoginUrl不为login.jsp(AccessControlFilter.DEFAULT_LOGIN_URL),则保留customAuthenticationFilter的loginUrl。反之,则将customAuthenticationFilter中的loginUrl设置为ShiroFilterFactoryBean中的loginUrl。

applySuccessUrlIfNecessary

    private void applySuccessUrlIfNecessary(Filter filter) {        String successUrl = getSuccessUrl();        if (StringUtils.hasText(successUrl) && (filter instanceof AuthenticationFilter)) {            AuthenticationFilter authcFilter = (AuthenticationFilter) filter;            //only apply the successUrl if they haven't explicitly configured one already:            String existingSuccessUrl = authcFilter.getSuccessUrl();            if (AuthenticationFilter.DEFAULT_SUCCESS_URL.equals(existingSuccessUrl)) {                authcFilter.setSuccessUrl(successUrl);            }        }    }

该函数与applyLoginUrlIfNecessary类似,选择设置或者保留customAuthenticationFilter中的successUrl。默认的AuthenticationFilter.DEFAULT_SUCCESS_URL为”/”(一般为首页)。

applyUnauthorizedUrlIfNecessary

    private void applyUnauthorizedUrlIfNecessary(Filter filter) {        String unauthorizedUrl = getUnauthorizedUrl();        if (StringUtils.hasText(unauthorizedUrl) && (filter instanceof AuthorizationFilter)) {            AuthorizationFilter authzFilter = (AuthorizationFilter) filter;            //only apply the unauthorizedUrl if they haven't explicitly configured one already:            String existingUnauthorizedUrl = authzFilter.getUnauthorizedUrl();            if (existingUnauthorizedUrl == null) {                authzFilter.setUnauthorizedUrl(unauthorizedUrl);            }        }    }

这个函数也和前面两个函数类似,这里就不详细说明了。
好了,关于ShiroFilterFactoryBean的构建大概就这样了,下面一节会开始介绍框架是如何调用的,例如loginUrl是在哪被使用的,以及实现一个自定义的FormAuthenticationFilter。

0 0
原创粉丝点击