,好记性不如烂笔头
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 Mapobjects; 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 Mapobjects; 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 AbstractFactoryimplements 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 IniFactorySupportextends 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 MapcreateDefaults(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 MapcreateInstanceMap(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; }}