鲁春利的工作笔记,好记性不如烂笔头



IniWebEnvironment

    在通过JavaSE方式解析权限信息时,是通过IniSecurityManagerFactory来实现的,其中也指定了默认的ini文件路径(IniFactorySupport):

public static final String DEFAULT_INI_RESOURCE_PATH = "classpath:shiro.ini";


    IniWebEnvironment提供了在JavaWeb下加载解析权限信息的方式。

package org.apache.shiro.web.env;
 /**
 * 通过ini配置文件或资源路径实现WebEnvironment配置功能
 */
public class IniWebEnvironment extends ResourceBasedWebEnvironment implements Initializable, Destroyable {
    public static final String DEFAULT_WEB_INI_RESOURCE_PATH = "/WEB-INF/shiro.ini";
    
    private Ini ini;
    
    public void init() {
        // ini未被设置过,getIni获取到null
        Ini ini = getIni();

        // 在EnvironmentLoaderListener分析时,通过ResourceBasedWebEnvironment.setConfigLocations设置了config文件路径
        String[] configLocations = getConfigLocations();

        if (log.isWarnEnabled() && !CollectionUtils.isEmpty(ini) && configLocations != null && configLocations.length > 0) {
            // 告警信息:ini已经被配置了,但config也被配置了,暂时IniWebEnvironment不支持同时配置,不过将来可能会支持
        }

        // 通过configLocations路径对应的ini文件创建Ini实例
        if (CollectionUtils.isEmpty(ini)) {
            log.debug("Checking any specified config locations.");
            ini = getSpecifiedIni(configLocations);
        }

        // 如果通过configLocations路径配置的ini文件创建Ini实例失败
        if (CollectionUtils.isEmpty(ini)) {
            log.debug("No INI instance or config locations specified.  Trying default config locations.");
            ini = getDefaultIni();    // 获取默认的Ini
        }

        if (CollectionUtils.isEmpty(ini)) {
            // 实例化ini对象失败,可能是configLocations路径配置错误或配置的文件无效
        }

        // 到了这里再进行set,设置ini实例
        setIni(ini);

        // 真正的权限解析及过滤的代码实现
        configure();
    }
    
    protected void configure() {
        // this.objects为父类DefaultEnvironment的变量protected final Map<String, Object> objects;
        this.objects.clear();

        WebSecurityManager securityManager = createWebSecurityManager();
        // 父类DefaultWebEnvironment的setWebSecurityManager方法,会继续调用其父类DefaultEnvironment
        setWebSecurityManager(securityManager);
        // ShiroFilter中setSecurityManager(env.getWebSecurityManager());也清晰了

        // 实际得到的是PathMatchingFilterChainResolver
        FilterChainResolver resolver = createFilterChainResolver();
        if (resolver != null) {    // 调用DefaultWebEnvironment.setObject方法进行设置,在ShiroFilter中就可以获取到了
            setFilterChainResolver(resolver);
        }
    }
    
    // 创建SecurityManager(DefaultWebSecurityManager)
    protected WebSecurityManager createWebSecurityManager() {
        // public class WebIniSecurityManagerFactory extends IniSecurityManagerFactory
        // 和在JavaSE环境下使用IniSecurityManagerFactory的方式一样了
        WebIniSecurityManagerFactory factory;
        Ini ini = getIni();
        if (CollectionUtils.isEmpty(ini)) {
            factory = new WebIniSecurityManagerFactory();
        } else {
            factory = new WebIniSecurityManagerFactory(ini);
        }
        
        WebSecurityManager wsm = (WebSecurityManager)factory.getInstance();

        //SHIRO-306 - get beans after they've been created (the call was before the factory.getInstance() call,
        //which always returned null.
        Map<String, ?> beans = factory.getBeans();
        if (!CollectionUtils.isEmpty(beans)) {
            this.objects.putAll(beans);
        }

        return wsm;
    }
    
    // 根据configLocations配置的ini文件生成ini实例(预处理configLocations)
    protected Ini getSpecifiedIni(String[] configLocations) throws ConfigurationException {

        Ini ini = null;

        if (configLocations != null && configLocations.length > 0) {
            if (configLocations.length > 1) {
                // 告警信息:配置了多个shiro.ini文件,但是只有第一个会被使用,现在还不支持配置多个,将来可能支持
            }

            //required = true, as it is user specified : 创建失败抛出ConfigurationException异常
            ini = createIni(configLocations[0], true);
        }

        return ini;
    }
    
    // 根据configLocations配置的ini文件生成ini实例(真正生成ini的操作)
    protected Ini createIni(String configLocation, boolean required) throws ConfigurationException {

        Ini ini = null;

        if (configLocation != null) {
            // convertPathToIni中以流的形式加载configLocation文件(ResourceUtils.getInputStreamForPath(path))
            // configLocation在web.xml的配置支持classpath:、url:、file:等多种形式。
            // ini = new Ini(); ini.load(is);    // 在ini的load方法中会解析ini文件(如[main]、[urls]、[filters])并进行封装
            ini = convertPathToIni(configLocation, required);
        }
        if (required && CollectionUtils.isEmpty(ini)) {
            // 抛出异常,ini文件没有找到无法创建ini实例
        }

        return ini;
    }
    
    // 如果通过指定的configLocations无法创建Ini实例,则使用默认加载路径尝试生成Ini实例
    protected Ini getDefaultIni() {

        Ini ini = null;
        // 获取系统默认ini文件路径
        String[] configLocations = getDefaultConfigLocations();
        if (configLocations != null) {
            for (String location : configLocations) {
                // required = false,系统默认ini文件创建Ini实例,创建失败日志会记录但不会抛异常
                ini = createIni(location, false);
                if (!CollectionUtils.isEmpty(ini)) {
                    log.debug("Discovered non-empty INI configuration at location '{}'.  Using for configuration.",
                            location);
                    break;
                }
            }
        }

        return ini;
    }
    
    // DEFAULT_WEB_INI_RESOURCE_PATH : /WEB-INF/shiro.ini
    // IniFactorySupport.DEFAULT_INI_RESOURCE_PATH :classpath:shiro.ini
    protected String[] getDefaultConfigLocations() {
        return new String[]{
            DEFAULT_WEB_INI_RESOURCE_PATH,
            IniFactorySupport.DEFAULT_INI_RESOURCE_PATH
        };
    }
    
    // 创建FilterChain包装器
    protected FilterChainResolver createFilterChainResolver() {

        FilterChainResolver resolver = null;
        // 已经setIni过了,因此这里能够取到有效的ini实例
        Ini ini = getIni();

        if (!CollectionUtils.isEmpty(ini)) {
            // 如果ini文件中配置了filters或者urls,创建resolver
            //only create a resolver if the 'filters' or 'urls' sections are defined:
            // 判断ini文件中是否包含urls元素
            Ini.Section urls = ini.getSection(IniFilterChainResolverFactory.URLS);
            // 判断ini文件中是否包含filters元素
            Ini.Section filters = ini.getSection(IniFilterChainResolverFactory.FILTERS);
            // 生成FilterChainResolver实例
            if (!CollectionUtils.isEmpty(urls) || !CollectionUtils.isEmpty(filters)) {
                //either the urls section or the filters section was defined.  Go ahead and create the resolver:
                IniFilterChainResolverFactory factory = new IniFilterChainResolverFactory(ini, this.objects);
                resolver = factory.getInstance();
            }
        }
        // PathMatchingFilterChainResolver
        return resolver;
    }
}


objects对象是在父类中定义的

package org.apache.shiro.env;

public class DefaultEnvironment implements NamedObjectEnvironment, Destroyable {
    public static final String DEFAULT_SECURITY_MANAGER_KEY = "securityManager";

    // 这里变量的定义是final
    protected final Map<String, Object> objects;
    private String securityManagerName;
    
    public DefaultEnvironment() {
        this(new ConcurrentHashMap<String, Object>());
    }
    
    @SuppressWarnings({"unchecked"})
    public DefaultEnvironment(Map<String, ?> seed) {
        this.securityManagerName = DEFAULT_SECURITY_MANAGER_KEY;
        if (seed == null) {
            throw new IllegalArgumentException("Backing map cannot be null.");
        }
        // final的变量可以在构造方法中进行赋值
        this.objects = (Map<String, Object>) seed;
    }
    
    // securityManager等对象都会存入到这个objects对象中
}


Apache Shiro学习笔记(二)身份验证获取SecurityManager

  • WebSecurityManager wsm = (WebSecurityManager)factory.getInstance();

    WebIniSecurityManagerFactory类的getInstance方法实际是父类的AbstractFactory的方法

package org.apache.shiro.util;

/**
 * TODO - Class JavaDoc
 *
 * @since 1.0
 */
public abstract class AbstractFactory<T> implements Factory<T> {

    private boolean singleton;
    private T singletonInstance;

    public AbstractFactory() {
        this.singleton = true;
    }

    public boolean isSingleton() {
        return singleton;
    }

    public void setSingleton(boolean singleton) {
        this.singleton = singleton;
    }

    public T getInstance() {
        T instance;
        if (isSingleton()) {
            if (this.singletonInstance == null) {
                this.singletonInstance = createInstance();
            }
            instance = this.singletonInstance;
        } else {
            instance = createInstance();
        }
        if (instance == null) {
            String msg = "Factory 'createInstance' implementation returned a null object.";
            throw new IllegalStateException(msg);
        }
        return instance;
    }

    protected abstract T createInstance();
}


  • 调用子类的IniFactorySupport.createInstance方法

package org.apache.shiro.config;

public abstract class IniFactorySupport<T> extends AbstractFactory<T> {

    public T createInstance() {
        Ini ini = resolveIni();

        T instance;

        if (CollectionUtils.isEmpty(ini)) {
            log.debug("No populated Ini available.  Creating a default instance.");
            instance = createDefaultInstance();
            if (instance == null) {
                String msg = getClass().getName() + " implementation did not return a default instance in " +
                        "the event of a null/empty Ini configuration.  This is required to support the " +
                        "Factory interface.  Please check your implementation.";
                throw new IllegalStateException(msg);
            }
        } else {
            log.debug("Creating instance from Ini [" + ini + "]");
            instance = createInstance(ini);
            if (instance == null) {
                String msg = getClass().getName() + " implementation did not return a constructed instance from " +
                        "the createInstance(Ini) method implementation.";
                throw new IllegalStateException(msg);
            }
        }

        return instance;
    }
        // 抽象类,需要调用子类的实现
    protected abstract T createInstance(Ini ini);
        // 抽象类,需要调用子类的实现
    protected abstract T createDefaultInstance();
}


  • 子类IniSecurityManagerFactory的createInstance与createDefaultInstance方法

package org.apache.shiro.config;

public class IniSecurityManagerFactory extends IniFactorySupport<SecurityManager> {

    protected SecurityManager createDefaultInstance() {
        return new DefaultSecurityManager();
    }

    protected SecurityManager createInstance(Ini ini) {
        if (CollectionUtils.isEmpty(ini)) {
            throw new NullPointerException("Ini argument cannot be null or empty.");
        }
        SecurityManager securityManager = createSecurityManager(ini);
        if (securityManager == null) {
            String msg = SecurityManager.class + " instance cannot be null.";
            throw new ConfigurationException(msg);
        }
        return securityManager;
    }
    
    private SecurityManager createSecurityManager(Ini ini) {
        Ini.Section mainSection = ini.getSection(MAIN_SECTION_NAME);
        if (CollectionUtils.isEmpty(mainSection)) {
            //try the default:
            mainSection = ini.getSection(Ini.DEFAULT_SECTION_NAME);
        }
        return createSecurityManager(ini, mainSection);
    }
     // 同样需要创建SecurityManager的实例
    @SuppressWarnings({"unchecked"})
    private SecurityManager createSecurityManager(Ini ini, Ini.Section mainSection) {
        // 注意:getInstance方法是由WebIniSecurityManagerFactory触发的,因此这里的createDefaults会先调用子类的createDefaults
        // 与Apache Shiro学习笔记(二)身份验证获取SecurityManager 不一致了
        Map<String, ?> defaults = createDefaults(ini, mainSection);
        Map<String, ?> objects = buildInstances(mainSection, defaults);

        SecurityManager securityManager = getSecurityManagerBean();

        boolean autoApplyRealms = isAutoApplyRealms(securityManager);

        if (autoApplyRealms) {
            //realms and realm factory might have been created - pull them out first so we can
            //initialize the securityManager:
            Collection<Realm> realms = getRealms(objects);
            //set them on the SecurityManager
            if (!CollectionUtils.isEmpty(realms)) {
                applyRealmsToSecurityManager(realms, securityManager);
            }
        }

        return securityManager;
    }
}


  • 在父类中还是需要子类的WebIniSecurityManagerFactory.createDefaults方法,在这个方法中会加载Shiro的默认filter

package org.apache.shiro.web.config;

public class WebIniSecurityManagerFactory extends IniSecurityManagerFactory {
    @SuppressWarnings({"unchecked"})
    @Override
    protected Map<String, ?> createDefaults(Ini ini, Ini.Section mainSection) {
        // 调用IniSecurityManagerFactory.createDefaults
        Map defaults = super.createDefaults(ini, mainSection);
        //add the default filters:添加默认的拦截器,如authc、ssl等
        Map<String, Filter> defaultFilters = DefaultFilter.createInstanceMap(null);
        defaults.putAll(defaultFilters);
        return defaults;
    }
}


  • 内置的拦截器基于枚举来实现

package org.apache.shiro.web.filter.mgt;

import org.apache.shiro.util.ClassUtils;
import org.apache.shiro.web.filter.authc.*;
import org.apache.shiro.web.filter.authz.*;
import org.apache.shiro.web.filter.session.NoSessionCreationFilter;

import javax.servlet.Filter;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import java.util.LinkedHashMap;
import java.util.Map;

/**
 * Enum representing all of the default Shiro Filter instances available to web applications.  Each filter instance is
 * typically accessible in configuration the {@link #name() name} of the enum constant.
 *
 * @since 1.0
 */
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 init default filter instance of type " +
                            filter.getClass().getName();
                    throw new IllegalStateException(msg, e);
                }
            }
            filters.put(defaultFilter.name(), filter);
        }
        return filters;
    }
}