,好记性不如烂笔头


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

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

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

package org.apache.shiro.util;/** * TODO - Class JavaDoc * * @since 1.0 */public abstract class AbstractFactory
 implements Factory
 {    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
 extends AbstractFactory
 {    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
 {    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
 defaults = createDefaults(ini, mainSection);        Map
 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
 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
 createDefaults(Ini ini, Ini.Section mainSection) {        // 调用IniSecurityManagerFactory.createDefaults        Map defaults = super.createDefaults(ini, mainSection);        //add the default filters:添加默认的拦截器,如authc、ssl等        Map
 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
 filterClass;    private DefaultFilter(Class
 filterClass) {        this.filterClass = filterClass;    }    public Filter newInstance() {        return (Filter) ClassUtils.newInstance(this.filterClass);    }    public Class
 getFilterClass() {        return this.filterClass;    }    public static Map
 createInstanceMap(FilterConfig config) {        Map
 filters = new LinkedHashMap
(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;    }}